📜 ⬆️ ⬇️

Using xslt templates in real projects

In the article, you will not find comparative tests of template engines. But you will find information about using xslt as a template engine on real projects. The possibilities of named templates, the use of template functions, reference books are considered.

1. Project structure


Usually a website page consists of several common blocks (menu, footer, ...) and a content part, which for generality will be called the main block. All these blocks are placed inside some index template, which knows in which place which block to display: the menu should be at the top, the main block in the center, and the footer at the bottom.

We get the following structure
/themes -     /themes/index/main.xsl -   /themes/models/user.xsl -  ,      /themes/inc/functions.xsl -  - /themes/blocks/footer.xsl -   /themes/blocks/menu.xsl -   /themes/cabinet/main.xsl -        

')
The controller of the user account home page works as follows:
  1. gets the data for the main block, processes it using /themes/cabinet/main.xsl and puts the result (ready html) into the final xml
  2. similarly processes data for other blocks (menu, footer) and puts the result in xml
  3. the final xml, in which the data of all the blocks are located, processes using the index template /themes/index/main.xsl and gives the result to the user in the form of html.


The index template /themes/index/main.xsl can look like this:
 <xsl:template match="page"> <head> <title><xsl:value-of select="title" /></title> </head> <body> <div class="page-container"> <xsl:value-of select="blocks/menu_top/html" disable-output-escaping="yes"/> <div class="main"> <xsl:value-of select="blocks/content/html" disable-output-escaping="yes"/> </div> <xsl:value-of select="blocks/footer/html" disable-output-escaping="yes"/> </div> </body> </xsl:template> 


2. Named patterns


The xslt template accepts data as an xml document. This is convenient in that we can operate with whole nodes. For example, to display the user name we may have such a pattern
 <xsl:template name="inc_show_user"> <xsl:param name="user"/> <img src="/img/{$user/userpic}.png"/> <xsl:value-of select="concat($user/first_name, ' ', $user/last_name)"/> </xsl:template> 
which is located in the /themes/models/user.xsl file.

We can use this template to display the current user.
 <xsl:call-template name="inc_show_user"> <xsl:with-param name="user" select="/*/cur_user"/> </xsl:call-template> 

and to display a list of users
 <xsl:for-each select="users/item"> <xsl:call-template name="inc_show_user"> <xsl:with-param name="user" select="."/> </xsl:call-template> </xsl:for-each> 


Such a unity of the display of entities allows you to quickly change their display. Of course, not all users have a picture, and therefore it should not be displayed for everyone.
 <xsl:template name="inc_show_user"> <xsl:param name="user"/> <xsl:choose> <xsl:when test="$user/userpic>0"> <img src="/img/{$user/userpic}.png"/> </xsl:when> <xsl:otherwise> <img src="/img/default.png"/> </xsl:otherwise> </xsl:choose> <xsl:value-of select="concat($user/first_name, ' ', $user/last_name)"/> </xsl:template> 


3. Import templates


In order to have access to the display of the “user” entity in the block template, we must include the file /themes/models/user.xsl.
For the /themes/cabinet/main.xsl template, the connection will look like this
 <xsl:import href="../models/user.xsl"/> 
(xsl: import should be described immediately after xsl: stylesheet)

4. Not a single line of php-code in the view


The MVC pattern involves the separation of model, logic, and presentation. The application logic requests the necessary data from the model and passes it to the view. The view must receive the necessary amount of data to display it to the user. Those. in the view, we should only output them and not have to transform the data in any way. We should not get the username by their id, should not get the current time, etc. All these data should already be available for submission. If there is not enough data, then the controller must provide it.

Xslt allows you to perform simple data operations: comparison, counting, sorting, formatting numbers, rounding, arithmetic operations, concatenation, ... It would seem that this contradicts the previous paragraph. But let me say that as a result of all these operations, we do not receive new data, but only transform the existing data.

Not always have all the necessary means to obtain the desired result. For example, output the ending for a number I think many have a similar function.
 function str_plural_form($n, $form1='', $form2='', $form5=''){ $lastN=$num%10; $lastT=$num%100; if($lastT>=10 && $lastT<=20){ return $form5; } switch ($lastN){ case 1: return $form1; case 2: case 3: case 4: return $form2; default: return $form5; } } 

And even more, xslt allows you to call this function directly from a template.
 <xsl:value-of select="php:function('str_plural_form', 1*$cnt_users, '', '', '')"/> 

But this is not only contrary to the section title, but is also a kind of atavism. It is better to avoid calling php functions inside xslt templates.
What to do? There are 2 exits:
  1. let the controller call str_plural_form and give the necessary data
  2. make a named template function that we put in /themes/inc/functions.xsl

 <xsl:template name="f_plural_form"> <xsl:param name="num"></xsl:param> <xsl:param name="format">### ###</xsl:param> <xsl:param name="is_show_num">1</xsl:param> <xsl:param name="space"/> <xsl:param name="str1"></xsl:param> <xsl:param name="str2"></xsl:param> <xsl:param name="str5"></xsl:param> <xsl:if test="$is_show_num=1"> <xsl:value-of select="format-number($num, $format)"/> <xsl:choose> <xsl:when test="$space!=''"> <xsl:value-of select="$space" disable-output-escaping="yes"/> </xsl:when> <xsl:otherwise> <xsl:text> </xsl:text> </xsl:otherwise> </xsl:choose> </xsl:if> <xsl:variable name="lastN" select="$num mod 10"/> <xsl:variable name="lastT" select="$num mod 100"/> <xsl:choose> <xsl:when test="$lastT>=10 and 20>=$lastT"> <xsl:value-of select="$str5" disable-output-escaping="yes"/> </xsl:when> <xsl:when test="$lastN=1"> <xsl:value-of select="$str1" disable-output-escaping="yes"/> </xsl:when> <xsl:when test="$lastN=2 or $lastN=3 or $lastN=4"> <xsl:value-of select="$str2" disable-output-escaping="yes"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$str5" disable-output-escaping="yes"/> </xsl:otherwise> </xsl:choose> </xsl:template> 


The function call will look like this.
 <xsl:call-template name="f_plural_form"> <xsl:with-param name="is_show_num">1</xsl:with-param> <xsl:with-param name="num" select="$cnt_users"/> <xsl:with-param name="str1"></xsl:with-param> <xsl:with-param name="str2"></xsl:with-param> <xsl:with-param name="str5"></xsl:with-param> </xsl:call-template> 


5. Directories


Let us return to the output of user information. For example, on the forum page we need to display


You can solve the problem vlob. When receiving each of the lists, do LEFT JOIN users and get the necessary data to display information about the user. But there are negative points of such a decision. The first is the possible data redundancy (users from the lists can be repeated), the second is the additional load on the sql server.

Another solution to the problem. Get all listings. Then from these lists get the set user_id. And for this set, make one query to the users table. The result is added in xml at a known address, for example / ref_users.
As a result, we should have an xml document with the posts posts, active_users, online_users, ref_users.

To display information about the user, we will make such a named template.
 <xsl:template name="inc_show_user_by_id"> <xsl:param name="user_id"/> <!--       id --> <xsl:variable name="cur_user" select="/*/ref_users/item[user_id=$user_id]"/> <xsl:call-template name="inc_show_user"> <xsl:with-param name="user" select="$cur_user"/> </xsl:call-template> </xsl:template> 
and save it in /themes/models/user.xsl. This is a template for the user's output by his id.

You can display a list of posts with user information
 <xsl:for-each select="posts/item"> <xsl:call-template name="inc_show_user_by_id"> <!--    user_id   --> <xsl:with-param name="user_id" select="user_id"/> </xsl:call-template> <!--     --> <!-- ... --> </xsl:for-each> 


Conclusion


The article turned out to be voluminous, that is why questions of organization of templates for ajax, “abstract templates”, support for several languages ​​were not considered. As well as issues of speed and caching.

Source: https://habr.com/ru/post/151241/


All Articles