Minden, amit mindig is tudni akartál az {iterateWhile}
A {iterateWhile}
címke a foreach ciklusokban különböző trükkökre alkalmas.
Tegyük fel, hogy a következő adatbázis táblával rendelkezünk, ahol az elemek kategóriákba vannak osztva:
id | catId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Természetesen a foreach ciklusban az elemek listaként való kirajzolása egyszerű:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
De mit tegyünk, ha minden kategóriát külön listában szeretnénk megjeleníteni? Más szóval, hogyan oldjuk meg egy foreach ciklusban egy lineáris lista elemeinek csoportosítását. A kimenetnek így kellene kinéznie:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Megmutatjuk, milyen egyszerűen és elegánsan megoldható a feladat az iterateWhile segítségével:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
Míg a {foreach}
a ciklus külső részét jelöli, azaz az egyes kategóriákhoz tartozó listák rajzolását,
addig a {iterateWhile}
címkék a belső részt, azaz az egyes elemeket jelölik. Az end tagben lévő feltétel azt
mondja, hogy az ismétlés addig folytatódik, amíg az aktuális és a következő elem ugyanahhoz a kategóriához tartozik
($iterator->nextValue
a következő elem).
Ha a feltétel mindig teljesül, akkor a belső ciklusban minden elem kirajzolódik:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
Az eredmény így fog kinézni:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Mire jó az iterateWhile ilyen használata? Miben különbözik attól a megoldástól, amit a bemutató legelején mutattunk?
A különbség az, hogy ha a táblázat üres, és nem tartalmaz semmilyen elemet, akkor nem fog üresen renderelni
<ul></ul>
.
Megoldás a {iterateWhile}
nélkül
Ha ugyanazt a feladatot a sablonrendszerek teljesen alapvető konstrukcióival oldanánk meg, például Twigben, Blade-ben vagy tiszta PHP-ben, a megoldás valahogy így nézne ki:
{var $prevCatId = null}
{foreach $items as $item}
{if $item->catId !== $prevCatId}
{* the category has changed *}
{* we close the previous <ul>, if it is not the first item *}
{if $prevCatId !== null}
</ul>
{/if}
{* új listát nyitunk *}
<ul>
{do $prevCatId = $item->catId}
{/if}
<li>{$item->name}</li>
{/foreach}
{if $prevCatId !== null}
{* lezárjuk az utolsó listát *}
</ul>
{/if}
Ez a kód azonban érthetetlen és nem intuitív. A nyitó és záró HTML-címkék közötti kapcsolat egyáltalán nem
egyértelmű. Első pillantásra nem egyértelmű, hogy hiba van-e benne. És olyan segédváltozókat igényel, mint a
$prevCatId
.
Ezzel szemben a {iterateWhile}
megoldása tiszta, egyértelmű, nem igényel segédváltozókat, és
bolondbiztos.
Feltétel a záró tagben
Ha a nyitó tagben adunk meg egy feltételt {iterateWhile}
, a viselkedés megváltozik: a feltétel (és a
következő elemre való továbblépés) a belső ciklus elején hajtódik végre, nem pedig a végén. Így míg a
{iterateWhile}
feltétel nélkül mindig belép, a {iterateWhile $cond}
csak akkor lép be, ha a
$cond
feltétel teljesül. Ezzel egyidejűleg a következő elemet írja a $item
.
Ez hasznos például olyan helyzetekben, amikor az egyes kategóriák első elemét másképp szeretnénk megjeleníteni, például:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Módosítsuk az eredeti kódot, rajzoljuk az első elemet, majd további elemeket ugyanabból a kategóriából a belső
ciklusban {iterateWhile}
:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->catId === $iterator->nextValue->catId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Beágyazott hurkok
Egy ciklusban több belső hurkot is létrehozhatunk, és akár egymásba is ágyazhatjuk őket. Így például alkategóriákat csoportosíthatunk.
Tegyük fel, hogy van egy másik oszlop a subCatId
táblázatban, és amellett, hogy minden kategória egy
különálló <ul>
, minden alkategória egy külön oszlopban lesz. <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}
Szűrő |batch
A lineáris elemek csoportosítását szintén a batch
szűrő biztosítja, mégpedig fix elemszámú
tételekbe:
<ul>
{foreach ($items|batch:3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Ez helyettesíthető iterateWhile-val az alábbiak szerint:
<ul>
{foreach $items as $item}
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>