Все, що ви завжди хотіли знати про {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}
вказують на
внутрішню частину, тобто на окремі елементи. Умова в тезі end говорить,
що повторення триватиме доти, доки поточний і наступний елемент
належать до однієї категорії ($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>