4. More nesting

OPT does not limit the number of nested sections, but in case of deeper nesting we need a way to control it more precisely. Sometimes we do not want the section to be automatically connected with the parent. The template language provides us the parent attribute which allows us to control the relations between sections.

<ol>
<opt:section name="top">
  <li>Top: {$top.text}<ol>
  <opt:section name="middle">
    <li>Middle: {$middle.text}<ol>
    <opt:section="bottom" parent="top">
      <li>Bottom: {$bottom.text}</li>
    </opt:section>
    </ol></li>
  </opt:section>
  </ol></li>
</opt:section>
</ol>

Our deepest section was intentionally linked with the top-level section, avoiding the "natural" parent, middle. It means that the data must look like this:

$view = new Opt_View('sections.tpl');
$view->top = array(0 =>
  array(
   'text' => 'Top 1'
   'middle' => array(0 =>
     array('text' => 'Middle 1'),
     array('text' => 'Middle 2'),
   ),
   // elements of "bottom" belong to "top", not "middle".
   'bottom' => array(0 =>
     array('text' => 'Bottom 1'),
     array('text' => 'Bottom 2'),
   ),
  ),
  array(
   'text' => 'Top 2'
   'middle' => array(0 =>
     array('text' => 'Middle 1a'),
     array('text' => 'Middle 2a'),
   ),
   'bottom' => array(0 =>
     array('text' => 'Bottom 1a'),
     array('text' => 'Bottom 2a'),
   ),
  )
);
$view->setFormat('middle', 'SingleArray');
$view->setFormat('bottom', 'SingleArray');

Without the parent attribute, the elements of bottom section would have to be defined for each middle element separately. After rendering, our list should look like this:

  1. Top 1
    1. Middle 1
      1. Bottom 1
      2. Bottom 2
    2. Middle 2
      1. Bottom 1
      2. Bottom 2
  2. Top 2
    1. Middle 1a
      1. Bottom 1a
      2. Bottom 2a
    2. Middle 2a
      1. Bottom 1a
      2. Bottom 2a

The attribute has one more usage, this time shown on a practical example. We want to create a universal template for our control panel that would handle displaying the data from different database tables. Each element is equipped with some options, such as "Edit" or "Remove", but for different tables, we may need different option sets: sometimes the user should not be able to remove a row, otherwise - we want to give him the ability to do something extra. The simplest solution is to pack the option list to a section, but normally it would be automatically connected with the row displaying section and we would be forced to add the same options to each element separately. Of course we do not want the options to be connected with rows and we can achieve that by adding an empty parent attribute to the instruction.

<opt:section name="items">
<tr>
  <td>{$items.id}</td>
  <td>{$items.title}</td>
  <td><opt:section="options" parent="">
    <a parse:href="buildUrl($options.url, $items.id)">{$options.title}</a>
  </opt:section></td>
</tr>
</opt:section>

The code that generates the data for us:

$view = new Opt_View('universal_list.tpl');
$view->items = $model->generateList();
$view->options = array(0 =>
  array('url' => '/panel/edit/', 'title' => 'Edit'),
  array('url' => '/panel/remove/', 'title' => 'Remove'),
  array('url' => '/panel/search/', 'title' => 'Search'),
);

That's it. Now OPT treats both of the sections as they are not nested.

As an exercise we suggest solving the following problem: does the following code work and why or why not?

<opt:show name="top">
  <opt:section name="middle" parent="top">
    Element.
  </opt:section>
 
  <opt:section>
    List element.
  </opt:section>
</opt:show>