Vse, kar ste vedno želeli vedeti o {iterateWhile}

Oznaka {iterateWhile} je primerna za različne trike v ciklih foreach.

Recimo, da imamo naslednjo tabelo podatkovne zbirke, v kateri so predmeti razdeljeni v kategorije:

id catId name
1 1 Apple
2 1 Banana
3 2 PHP
4 3 Green
5 3 Red
6 3 Blue

Seveda je risanje elementov v zanki foreach kot seznam enostavno:

<ul>
{foreach $items as $item}
	<li>{$item->name}</li>
{/foreach}
</ul>

Kaj pa storiti, če želite vsako kategorijo prikazati na ločenem seznamu? Z drugimi besedami, kako rešiti nalogo združevanja elementov z linearnega seznama v ciklu foreach. Rezultat mora biti videti takole:

<ul>
	<li>Apple</li>
	<li>Banana</li>
</ul>

<ul>
	<li>PHP</li>
</ul>

<ul>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Pokazali vam bomo, kako enostavno in elegantno lahko to nalogo rešimo z iterateWhile:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}</li>
		{/iterateWhile $item->catId === $iterator->nextValue->catId}
	</ul>
{/foreach}

Medtem ko {foreach} označuje zunanji del cikla, tj. izris seznamov za vsako kategorijo, oznake {iterateWhile} označujejo notranji del, tj. posamezne elemente. Pogoj v končni oznaki pove, da se bo ponavljanje nadaljevalo, dokler trenutni in naslednji element pripadata isti kategoriji ($iterator->nextValue je naslednji element).

Če je pogoj vedno izpolnjen, so v notranjem ciklu narisani vsi elementi:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}
		{/iterateWhile true}
	</ul>
{/foreach}

Rezultat bo videti takole:

<ul>
	<li>Apple</li>
	<li>Banana</li>
	<li>PHP</li>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Kaj dobrega prinaša takšna uporaba iterateWhile? Kako se razlikuje od rešitve, ki smo jo prikazali na samem začetku tega učbenika? Razlika je v tem, da če je tabela prazna in ne vsebuje nobenih elementov, se ne bo prikazala prazna <ul></ul>.

Rešitev brez spletne strani {iterateWhile}

Če bi isto nalogo reševali s povsem osnovnimi konstrukcijami sistemov predlog, na primer v Twigu, Bladeu ali čistem PHP, bi bila rešitev videti nekako takole:

{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* kategorija se je spremenila *}

		{* zapremo prejšnji <ul>, če to ni prvi element *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* odpremo nov seznam *}
		<ul>

		{do $prevCatId = $item->catId}
	{/if}

	<li>{$item->name}</li>
{/foreach}

{if $prevCatId !== null}
	{* zapremo zadnji seznam *}
	</ul>
{/if}

Vendar je ta koda nerazumljiva in neintuitivna. Povezava med začetnimi in zaključnimi oznakami HTML sploh ni jasna. Na prvi pogled ni jasno, ali gre za napako. In zahteva pomožne spremenljivke, kot je $prevCatId.

V nasprotju s tem je rešitev s {iterateWhile} čista, jasna, ne potrebuje pomožnih spremenljivk in je zanesljiva.

Pogoj v zaključni oznaki

Če določimo pogoj v začetni oznaki {iterateWhile}, se obnašanje spremeni: pogoj (in prehod na naslednji element) se izvede na začetku notranjega cikla in ne na koncu. Tako se {iterateWhile} brez pogoja vedno vnese, {iterateWhile $cond} pa se vnese šele, ko je izpolnjen pogoj $cond. Hkrati se v $item zapiše naslednji element.

To je uporabno na primer v primeru, ko želite prvi element v vsaki kategoriji prikazati na drugačen način, npr:

<h1>Apple</h1>
<ul>
	<li>Banana</li>
</ul>

<h1>PHP</h1>
<ul>
</ul>

<h1>Green</h1>
<ul>
	<li>Red</li>
	<li>Blue</li>
</ul>

Spremenimo prvotno kodo, v notranji zanki izrišemo prvi element in nato dodatne elemente iz iste kategorije {iterateWhile}:

{foreach $items as $item}
	<h1>{$item->name}</h1>
	<ul>
		{iterateWhile $item->catId === $iterator->nextValue->catId}
			<li>{$item->name}</li>
		{/iterateWhile}
	</ul>
{/foreach}

Vgnezdene zanke

V enem ciklu lahko ustvarimo več notranjih zank in jih celo ugnezdimo. Na ta način lahko na primer združimo podkategorije.

Recimo, da je v preglednici še en stolpec subCatId in poleg tega, da je vsaka kategorija v ločenem <ul>, bo vsaka podkategorija v ločenem <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}

Filtriranje |batch

Združevanje linearnih elementov v skupine omogoča tudi filter batch, in sicer v serije s fiksnim številom elementov:

<ul>
{foreach ($items|batch:3) as $batch}
	{foreach $batch as $item}
		<li>{$item->name}</li>
	{/foreach}
{/foreach}
</ul>

Zamenjamo ga lahko z iterateWhile, kot sledi:

<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>
različica: 3.0