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>
versão: 3.0