3. Zagnieżdżanie sekcji

Wiemy już, jak działają sekcje oraz jak formaty danych wpływają na ich zachowanie, dlatego teraz przyjrzymy się nieco bardziej zaawansowanym konstrukcjom. Bardzo często na stronach internetowych pojawiają się listy zagnieżdżone:

  1. lista tagów przy wpisach na blogu,
  2. dwupoziomowe menu,
  3. lista kategorii oraz należących do nich działów na forum dyskusyjnym.

Ich modelowanie przy pomocy sekcji może wydawać się nieco skomplikowane na pierwszy rzut oka, lecz po zrozumieniu rządzącej tym reguły okaże się bajecznie proste. Rozpatrzmy pierwszy przypadek. Aby wyświetlić listę wpisów na blogu oraz skojarzonych z nimi tagów, wystarczy dwie sekcje po prostu zagnieździć w sobie:

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

W OPT nie trzeba pamiętać o tym, by zagnieżdżone sekcje ze sobą łączyć. Samo zagnieżdżenie dwóch sekcji w sobie jest dla kompilatora sygnałem, że w tym miejscu należy wyświetlić jedynie te tagi, które są skojarzone z aktualnie wyświetlanym wpisem. Dodatkowy atrybut str:separator w sekcji „tags” jest kolejnym ułatwieniem podczas pisania. Mówi on, że każde dwa tagi mają być oddzielone od siebie przecinkiem. Przy tym jest na tyle inteligentny, że nie wstawia przecinka przed pierwszym lub za ostatnim tagiem.

Ponieważ szablon dalej nie zawiera żadnych szczegółów technicznych, jak dwie sekcje łączyć ze sobą, łatwo domyślić się, że to także jest kontrolowane przez formaty danych. Pokażemy teraz, jak można przygotować dane dla takiej zagnieżdżonej listy w domyślnym formacie Array.

$view = new Opt_View('blog_index.tpl');
$view->entries = array(0 =>
  array('title' => 'Wpis 1', 'body' => 'Treść'),
  array('title' => 'Wpis 2', 'body' => 'Treść'),
  array('title' => 'Wpis 3', 'body' => 'Treść'),
);
$view->tags = array(0 =>
  // dane dla wpisu 1
  array(0 =>
   array('slug' => 'sport', 'title' => 'Sport'),
   array('slug' => 'wiadomosci', 'title' => 'Wiadomości'),
  ),
  // dane dla wpisu 2
  array(0 =>
   array('slug' => 'programowanie', 'title' => 'Programowanie'),
   array('slug' => 'php', 'title' => 'PHP'),
  ),
  // dane dla wpisu 3
  array(0 =>
   array('slug' => 'komputery', 'title' => 'Komputery'),
   array('slug' => 'linux', 'title' => 'Linux'),
   array('slug' => 'opensource', 'title' => 'Open source'),
  ),
);

Danych dla sekcji zagnieżdżonej nie pakujemy razem z wpisami. Zamiast tego, tagi muszą otrzymać swoją własną tablicę, odpowiednio pozagnieżdżaną. Jeśli $view->entries[1] oznacza wpis drugi, to $view->tags[1][0] będzie reprezentować pierwszy tag drugiego wpisu. Gdybyśmy mieli sekcje potrójnie zagnieżdżone, elementy najgłębszej sekcji musiałyby mieć odwołanie w stylu $view->najglebsza[1][2][3]. W podanym przykładzie reprezentuje ono czwarty najgłębszy element trzeciego elementu środkowego przypisanego z kolei do drugiego elementu nadrzędnego.

Jeśli korzystamy ze wzorca MVC, zwracanie danych w takim formacie może być wysoce niewygodne. Z pomocą przychodzi nam format SingleArray, który umożliwia nam zapakowanie danych dla wszystkich zagnieżdżonych sekcji do jednej, wielkiej tablicy, a poza tym działa identycznie, jak zwykły Array. Spójrzmy, jak z jego pomocą wypełnić szablon danymi:

$view = new Opt_View('blog_index.tpl');
$view->entries = array(0 =>
  array(
   'title' => 'Wpis 1',
   'body' => 'Treść',
   'tags' => array(0 =>
     array('slug' => 'sport', 'title' => 'Sport'),
     array('slug' => 'wiadomosci', 'title' => 'Wiadomości'),
   )
  ),
  array(
   'title' => 'Wpis 2',
   'body' => 'Treść',
   'tags' => array(0 =>
     array('slug' => 'programowanie', 'title' => 'Programowanie'),
     array('slug' => 'php', 'title' => 'PHP'),
   )
  ),
  array(
   'title' => 'Wpis 3',
   'body' => 'Treść',
   'tags' => array(0 =>
     array('slug' => 'komputery', 'title' => 'Komputery'),
     array('slug' => 'linux', 'title' => 'Linux'),
     array('slug' => 'opensource', 'title' => 'Open source'),
   )
  ),
);
$view->setFormat('tags', 'SingleArray');

Przykład ten jest także dobrą ilustracją tego, co mówiliśmy w poprzednim rozdziale. Dane dla sekcji domyślnie pochodzą ze zmiennej o identycznej nazwie, ale nie muszą. Tutaj nie ma zmiennej $tags. Zamiast tego nasz format danych szuka w aktualnie wyświetlanym elemencie sekcji nadrzędnej pola o nazwie tags i z niego pobiera dane. Warto zauważyć, że format SingleArray nie musi być ustawiony dla sekcji głównej, ponieważ oddziaływuje on jedynie na sekcje zagnieżdżone.

Czas na kolejne ćwiczenie i temat do przemyślenia, aby lepiej zrozumieć działanie sekcji zagnieżdżonych. Chcemy, by lista wpisów była obiektem, pojedynczy wpis tablicą, a tagi dla wpisu były pobierane przez generator. Jak ustawić formaty danych?