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>
verzió: 3.0