3. Nesting sections

We know how the sections work and how data formats modify their behaviour, but single sections were quite simple. In real world applications, we often encounter nested lists, such as:

  1. the blog posts and their tags,
  2. two-level menu,
  3. the list of categories and their forums on a discussion board.

Modelling the nested sections seems to be a bit more complicated at the first time, but once you understand a rule behind it, you will see that this is very easy, too. Let's take a look at the first situation. To display a list of blog posts and their tags, we just nest two sections one within another:

<opt:section name="entries">
<div class="entry">
  <h1>{$entries.title}</h1>
  <p class="tags">
   <opt:section name="tags" str:separator=", ">
    <span><a parse:href="'/tag/'~$tags.slug">{$tags.name}</a></span>
   </opt:section>
  </p>
  <p>{u:$entries.body}</p>
</div>
</opt:section>

In OPT, we do not have to remember about connecting the sections manually. The nesting is enough for the compiler to link them with a one-to-many relationship: it must list only those tags that belong to the currently displayed post. The extra attribute str:separator is another thing that makes our life easier. It puts a comma between every two tags and moreover it is smart enough not to put it before the first or after the last element.

Because the template still does not contain any technical details, how to connect the sections, it is very easy to notice that this process is also controlled by data formats. We will show now, how to prepare a data for the nested sections in the default Array format:

$view = new Opt_View('blog_index.tpl');
$view->entries = array(0 =>
  array('title' => 'Post 1', 'body' => 'Some content'),
  array('title' => 'Post 2', 'body' => 'Some content'),
  array('title' => 'Post 3', 'body' => 'Some content'),
);
$view->tags = array(0 =>
  // tags for post 1
  array(0 =>
   array('slug' => 'sport', 'title' => 'Sport'),
   array('slug' => 'news', 'title' => 'News'),
  ),
  // tags for post 2
  array(0 =>
   array('slug' => 'programming', 'title' => 'Programming'),
   array('slug' => 'php', 'title' => 'PHP'),
  ),
  // tags for post 3
  array(0 =>
   array('slug' => 'computers', 'title' => 'Computers'),
   array('slug' => 'linux', 'title' => 'Linux'),
   array('slug' => 'opensource', 'title' => 'Open source'),
  ),
);

We do not pack the data together with the posts. The tags must have their own array, but with more dimensions. If $view->entries[1] represents a second post, then $view->tags[1][0] will represent the first tag of the second post. If we had three sections, the elements of the deepest sections would have the following address: $view->deepest[1][2][3]: the fourth deepest element of the third middle element of the second top-level element.

If we are using the MVC pattern, returing the data in this format may be very problematic. This is why OPT provides another data format, SingleArray, where the data for the nested sections can be packed to one, huge array. Except this particular difference, the new format works in exactly the same way, as Array. Here, our script looks like this:

$view = new Opt_View('blog_index.tpl');
$view->entries = array(0 =>
  array(
   'title' => 'Post 1',
   'body' => 'Some content',
   'tags' => array(0 =>
     array('slug' => 'sport', 'title' => 'Sport'),
     array('slug' => 'news', 'title' => 'News'),
   )
  ),
  array(
   'title' => 'Post 2',
   'body' => 'Some content',
   'tags' => array(0 =>
     array('slug' => 'programming', 'title' => 'Programming'),
     array('slug' => 'php', 'title' => 'PHP'),
   )
  ),
  array(
   'title' => 'Post 3',
   'body' => 'Some content',
   'tags' => array(0 =>
     array('slug' => 'computers', 'title' => 'Computers'),
     array('slug' => 'linux', 'title' => 'Linux'),
     array('slug' => 'opensource', 'title' => 'Open source'),
   )
  ),
);
$view->setFormat('tags', 'SingleArray');

It is a good illustration of the information we have mentioned in the previous chapter: the section data do not have to come from a template variable. We do not have a $tags variable here. Instead, OPT looks for the tag data in the currently displayed element variables. It is worth noting that SingleArray does not have to be set for the top-level section, as it does not modify the behaviour of the top-level sections.

At the end, we have another exercise for you to help you better understand the topic. We would like the top-level post list to be an object, the posts - arrays, and the tags to be returned by a generator. How to set the data formats?