Всичко, което винаги сте искали да знаете за {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>