Всичко, което винаги сте искали да знаете за {iterateWhile}

Тагът {iterateWhile} е подходящ за различни трикове в цикли foreach.

Да предположим, че имаме следната таблица в базата данни, в която елементите са разделени на категории:

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

Разбира се, много е лесно да изведете елементите в цикъла foreach като списък:

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

Но какво да направите, ако искате да изведете всяка категория като отделен списък? С други думи, как се решава проблемът с групирането на елементи от линеен списък в цикъл foreach. Резултатът трябва да изглежда така:

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

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

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

Ще ви покажем как да решите този проблем лесно и елегантно с помощта на iterateWhile:

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

Докато {foreach} се отнася до външната част на цикъла, т.е. изготвянето на списъци за всяка категория, таговете {iterateWhile} се отнасят до вътрешната част, т.е. отделните елементи. Условието в крайния таг гласи, че повторението ще продължи, докато текущият и следващият елемент принадлежат към една и съща категория ($iterator->nextValue – следващ елемент).

Ако условието е винаги изпълнено, всички елементи се изтеглят във вътрешния цикъл:

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

Резултатът ще изглежда по следния начин:

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

Как е полезно да се използва iterateWhile по този начин? По какво се различава от решението, което показахме в началото на това ръководство? Разликата е, че ако таблицата е празна и не съдържа елементи, тя няма да се изведе празна <ul></ul>.

Решение без {iterateWhile}

Ако трябва да решим същия проблем, като използваме напълно елементарни шаблони, например в Twig, Blade или чист PHP, решението би изглеждало по следния начин

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

		{* we will open a new list *}
		<ul>

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

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

{if $prevCatId !== null}
	{* we close the last list *}
	</ul>
{/if}

Този код обаче е неразбираем и неинтуитивен. Връзката между началния и крайния HTML таг не е съвсем ясна. От пръв поглед не е ясно дали има грешка. А това изисква помощни променливи като $prevCatId.

За разлика от това решението с {iterateWhile} е чисто, ясно, не изисква помощни променливи и е надеждно.

Условие в затварящия таг

Ако посочите условие в отварящия таг {iterateWhile}, поведението се променя: условието (и преходът към следващия елемент) се изпълнява в началото на вътрешния цикъл, а не в края. Така, докато {iterateWhile} без условие се въвежда винаги, {iterateWhile $cond} се въвежда само когато е изпълнено условието $cond. В същото време следващият елемент се записва в $item.

Това е полезно например в ситуация, в която първият елемент във всяка категория трябва да се показва по различен начин, напр:

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

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

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

Променете изходния код, като визуализираме първия елемент и след това допълнителни елементи от същата категория във вътрешния цикъл {iterateWhile}:

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

Вложени цикли

Можем да създадем няколко вътрешни цикъла в един цикъл и дори да ги вложим един в друг. По този начин например подкатегориите могат да бъдат групирани заедно.

Да предположим, че в таблицата има още една колона subCatId и освен че всяка категория е в отделна колона, всяка подкатегория ще бъде в отделна колона. <ul>, всяка подкатегория ще бъде в отделна колона. <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}

Филтриране | партида

Групирането на елементите на реда се осигурява и от филтъра batch, в партида с фиксиран брой елементи:

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

Тя може да бъде заменена с iterateWhile, както следва:

<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>
версия: 3.0