Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /share/CACHEDEV1_DATA/Web/www/libraries/UBBcode/text_parser.class.php on line 228

Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /share/CACHEDEV1_DATA/Web/www/libraries/UBBcode/text_parser.class.php on line 228

Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /share/CACHEDEV1_DATA/Web/www/libraries/UBBcode/text_parser.class.php on line 228
Recursive template elements in Smarty & PHP

Comments Blog About Development Research Sites

Recursive template elements in Smarty & PHP

Jul 27, 2009
Although it is not common, sometimes template developers come accros situations where a certain element is needed recursively. Most frequently this concerns collapsing menu's or lists of content (for a book or forum with subfora for example). I wrote a short article on recursive iterators in PHP a while ago (here), but let's see now how to do it template wise.

First of all, there is an evergoing debate whether or not to use a template handler like Smarty. The argument in favor usually revolves around readability, while the counterargument most frequently heard states that PHP itself already is a template engine, which in a way is true - for a full discussion on this, see my article on template engines here. Now some say that complex data structures like a recursive array are a reason not to use a template handler. I say that on the contrary, they are an excelent reason to use them.

For the next options, consider a recursive, variable-depth array of data:
Code (php) (nieuw venster):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
array('title'     =>  'book A',
      'children'  =>  array(
        array(
'title'     =>  'Chapter 1',
              'children'  =>  array(
                array(
'title' =>  'Introduction'),
                array(
'title' =>  'About the author'))
        ),
        array(
'title'     =>  'Chapter 2',
              'children'  =>  array(
                array(
'title' =>  'The first steps'),
                array(
'title' =>  'Some more'),
                array(
'title' =>  'Continued')
                )
        )
      )
);


Option 1: static-depth manual recursion
This will be sufficient in almost all cases. Webpages only have a limited amount of room, people hate scrolling, so how many layers will you actually be showing? If the answer is less than three, the proposed data array can easily be displayed using several foreach loops inside eachother:
Code (php) (nieuw venster):
1
2
3
4
5
6
7
8
foreach ($books as $book) {
  echo
$book['title'];
  foreach (
$book as $chapter) {
    echo
$chapter['title']
    foreach (
$chapter as $subchapter)
     echo
$subchapter['title'];
  }
}

It is not exactly pretty (and we will get an E_NOTICE warning if a chapter does not have an array of subchapters - easily worked around, but inconvenient nonetheless) but it is by far the quickest solution to write and the easiest to maintain. The main drawback is repeating yourself on every level and having to adjust the dept manually in code.


Option 2: writing a recursive smarty plugin
Though relatively fast, this is a quite complex solution. You can store an output string in a static class member and call the same plugin function repeatedly (as you would a recursive method), but that invariably mixes HTML into your PHP code.


Option 3: Use Smarty's include / assignment plugin
There is an easier way though to bring recursiveness to Smarty. Wrap the client display object in its own file and include that file with a specific assignment - something like this:
Code (php) (nieuw venster):
1
2
3
4
5
6
7
8
{$parent.title}
<
ul>
  {foreach
from=$parent.children item=child}
    <
li>
      {include
file='this_foreach_block.tpl' parent=$child}
    <
/li>
  {
/foreach}
<
/ul>

Notice that during the include, we set the parent variable to the child node. This ensures that for the duration of that included block, $parent refers to a childnode of our original node - thus, its children are in turn granchildren of the original node, etc etc ad invinitum. With the correct data array this provides us with a nice, ever indenting ordered list of items - exactly how we wanted it! And, what is more, all layout is handled inside the template so it is easily updated by a designer.

There is a small drawback to that last approach: it requires an additional file include for each child. Now in itself this is quite fast (since diskcache and template compiling take care of most repeated tasks involved there), but with a great many nodes this will still slowdown page generation. If this is not acceptable either of the previous options can be used, though of course each with their own drawbacks.

FragFrog out!

New comment

Your name:
Comment: