Tudo o que você sempre quis saber sobre {alfabetizar-assim}
A etiqueta {iterateWhile}
é adequada para vários truques em ciclos anteriores.
Suponha que tenhamos a seguinte tabela de banco de dados, onde os itens são divididos em categorias:
id | catId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Naturalmente, desenhar itens em um laço na frente como uma lista é fácil:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Mas o que fazer se você quiser apresentar cada categoria em uma lista separada? Em outras palavras, como resolver a tarefa de agrupar itens de uma lista linear em um ciclo foreach. A saída deve se parecer com isto:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Mostraremos a facilidade e a elegância com que a tarefa pode ser resolvida com iteração:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
Enquanto {foreach}
marca a parte externa do ciclo, ou seja, o desenho de listas para cada categoria, as tags
{iterateWhile}
indicam a parte interna, ou seja, os itens individuais. A condição na etiqueta final diz que a
repetição continuará enquanto o elemento atual e o próximo elemento pertencerem à mesma categoria
($iterator->nextValue
é o próximo item).
Se a condição for sempre preenchida, então todos os elementos são desenhados no ciclo interno:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
O resultado será o seguinte:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
De que serve tal uso da iteração enquanto? De que forma difere da solução que mostramos logo no início deste tutorial?
A diferença é que se a tabela estiver vazia e não contiver nenhum elemento, ela não irá tornar vazia
<ul></ul>
.
Solução Sem {iterateWhile}
Se resolvêssemos a mesma tarefa com construções completamente básicas de sistemas de modelos, por exemplo em Twig, Blade ou PHP puro, a solução seria algo parecido com isto:
{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}
{* abriremos uma nova lista *}
<ul>
{do $prevCatId = $item->catId}
{/if}
<li>{$item->name}</li>
{/foreach}
{if $prevCatId !== null}
{* we close the last list *}
</ul>
{/if}
No entanto, este código é incompreensível e pouco intuitivo. A conexão entre as tags HTML de abertura e fechamento não é
clara em absoluto. Não é clara à primeira vista, se houver um erro. E requer variáveis auxiliares como
$prevCatId
.
Em contraste, a solução com {iterateWhile}
é limpa, clara, não necessita de variáveis auxiliares e é
infalível.
Condição na Etiqueta de Fechamento
Se especificarmos uma condição na etiqueta de abertura {iterateWhile}
, o comportamento muda: a condição (e
o avanço para o próximo elemento) é executada no início do ciclo interno, não no final. Assim, enquanto
{iterateWhile}
sem condição é sempre inserido, {iterateWhile $cond}
é inserido somente quando a
condição $cond
é cumprida. Ao mesmo tempo, o seguinte elemento é escrito para $item
.
Isto é útil, por exemplo, em uma situação em que você quer renderizar o primeiro elemento de cada categoria de uma maneira diferente, como por exemplo:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Vamos modificar o código original, primeiro desenhamos um item e depois itens adicionais da mesma categoria no laço interno
{iterateWhile}
:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->catId === $iterator->nextValue->catId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Laços aninhados
Podemos criar vários loops internos em um ciclo e até mesmo aninhá-los. Desta forma, por exemplo, as subcategorias poderiam ser agrupadas.
Suponha que haja outra coluna na tabela subCatId
e que, além de cada categoria estar em uma
<ul>
cada subcategoria estará em uma subcategoria separada <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}
Filtro |batch
O agrupamento de itens lineares também é fornecido por um filtro batch
, em lotes com um número fixo de
elementos:
<ul>
{foreach ($items|batch:3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Ela pode ser substituída por iteração, enquanto que, como se segue:
<ul>
{foreach $items as $item}
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>