4. Więcej zagnieżdżeń

OPT nie narzuca żadnych ograniczeń na ilość zagnieżdżeń sekcji, lecz przy większej ich ilości trzeba nad tym wszystkim jakoś zapanować. Ostatecznie nie zawsze chcemy, aby sekcja łączyła się automatycznie z jej sekcją nadrzędną. Tutaj pomoże nam atrybut parent pozwalający sterować powiązaniami:

<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>

Nasza najniższa sekcja została tutaj jawnie powiązana z sekcją najwyższą, przeskakując środkową middle. Oznacza to, że dane musimy przygotować w ten sposób:

$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'),
   ),
   // elementy „bottom” naleza do sekcji „top”, a nie „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');

Gdyby nie atrybut parent, elementy sekcji bottom musiałyby być definiowane dla każdego elementu middle. My jednak związaliśmy je z sekcją top, dlatego znajdują się one bezpośrednio tam. Nasza lista po wyświetleniu powinna wyglądać następująco:

  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

Inne zastosowanie atrybutu parent zademonstrujemy znów na przykładzie praktycznym. Tworzymy uniwersalny szablon dla naszego panelu administracyjnego, który będzie wyświetlać dane z wielu różnych tabel. Pojawia się pewien problem: w niektórych tabelach potrzebujemy opcji „Edytuj” i „Usuń” przy każdym elemencie, w innych musi być samo „Edytuj”, a w jeszcze innych należy dodać jeszcze „Komentarze”. Oczywiście najprostsze rozwiązanie to opakowanie listy opcji w sekcję, lecz przecież wtedy związałaby się ona automatycznie z listą elementów, przez co tablice z listą opcji musielibyśmy pakować do każdego z nich, co jest niepotrzebną stratą czasu. Rozwiązaniem jest ustawienie pustego atrybutu parent, dzięki czemu OPT potraktuje naszą sekcję z opcjami jak niezagnieżdżoną:

<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>

Dane dla naszej sekcji wyglądałyby następująco:

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

To wszystko. Nasze dwie zagnieżdżone sekcje działają tak, jakby były niezagnieżdżone.

Jako ćwiczenie pozostawiamy następujący problem: czy dany kod zadziała i dlaczego tak lub dlaczego nie?

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