Všechno, co jste kdy chtěli vědět o {iterateWhile}
Značka {iterateWhile}
se hodí na nejrůznější kejkle ve foreach cyklech.
Dejme tomu, že máme následující databázovou tabulku, kde jsou položky rozdělené do kategorií:
id | catId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Vykreslit položky ve foreach cyklu jako seznam je samozřejmě snadné:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Ale co kdybychom chtěli, aby každá kategorie byla v samostatném seznamu? Jinými slovy, řešíme úkol, jak seskupit položky v lineárním seznamu ve foreach cyklu do skupin. Výstup by měl vypadat takto:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Rovnou si ukážeme, jak snadno a elegantně se dá úkol vyřešit pomocí iterateWhile:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
Zatímco {foreach}
označuje vnější část cyklu, tedy vykreslování seznamů pro každou kategorii, tak
značka {iterateWhile}
označuje vnitřní část, tedy jednotlivé položky. Podmínka v koncové značce říká,
že opakování bude probíhat do té doby, dokud aktuální i následující prvek patří do stejné kategorie
($iterator->nextValue
je následující položka).
Kdyby podmínka byla splněná vždy, tak se ve vnitřním cyklu vykreslí všechny prvky:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
Výsledek bude vypadat takto:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
K čemu je takové použití iterateWhile dobré? Jak se liší od řešení, které jsme si ukázali úplně na začátku
tohoto návodu? Rozdíl je v tom, že když bude tabulka prázdná a nebude obsahovat žádné prvky, nevypíše se prázdné
<ul></ul>
.
Řešení bez {iterateWhile}
Pokud bychom stejný úkol řešili zcela základními prostředky šablonovacích systému, například v Twig, Blade, nebo čistém PHP, vypadalo by řešení cca takto:
{var $prevCatId = null}
{foreach $items as $item}
{if $item->catId !== $prevCatId}
{* změnila se kategorie *}
{* uzavřeme předchozí <ul>, pokud nejde o první položku *}
{if $prevCatId !== null}
</ul>
{/if}
{* otevřeme nový seznam *}
<ul>
{do $prevCatId = $item->catId}
{/if}
<li>{$item->name}</li>
{/foreach}
{if $prevCatId !== null}
{* uzavřeme poslední seznam *}
</ul>
{/if}
Tento kód je však nesrozumitelný a neintuitivní. Není vůbec jasná vazba mezi otevíracími a zavíracími HTML
značkami. Není na první pohled vidět, jestli tam není nějaká chyba. A vyžaduje pomocné proměnné jako
$prevCatId
.
Oproti tomu řešení s {iterateWhile}
je čisté, přehledné, nepotřebujeme pomocné proměnné a je
blbuvzdorné.
Podmínka v otevírací značce
Pokud uvedeme podmínku v otevírací značce {iterateWhile}
, tak se chování změní: podmínka (a přechod na
další prvek) se vykoná už na začátku vnitřního cyklu, nikoliv na konci. Tedy zatímco do {iterateWhile}
bez
podmínky se vstoupí vždy, do {iterateWhile $cond}
jen při splnění podmínky $cond
. A zároveň se
s tím do $item
zapíše následující prvek.
Což se hodí například v situaci, kdy budeme chtít první prvek v každé kategorii vykreslit jiným způsobem, například takto:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Původní kód upravíme tak, že nejprve vykreslíme první položku a poté ve vnitřním cyklu {iterateWhile}
vykreslíme další položky ze stejné kategorie:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->catId === $iterator->nextValue->catId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Vnořené smyčky
V rámci jednoho cyklu můžeme vytvářet více vnitřních smyček a dokonce je zanořovat. Takto by se daly seskupovat třeba podkategorie atd.
Dejme tomu, že v tabulce bude ještě další sloupec subCatId
a kromě toho, že každá kategorie bude
v samostatném <ul>
, každá každý podkategorie samostatném <ol>
:
{foreach $items as $item}
<ul>
{iterateWhile}
<ol>
{iterateWhile}
<li>{$item->name}
{/iterateWhile $item->subCatId === $iterator->nextValue->subCatId}
</ol>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
Filtr |batch
Seskupování lineárních položek obstarává také filtr batch
, a to do dávek s pevným počtem prvků:
<ul>
{foreach ($items|batch:3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Lze jej nahradit za iterateWhile tímto způsobem:
<ul>
{foreach $items as $item}
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>