Nette Documentation Preview

syntax
Tout ce que vous avez toujours voulu savoir sur {iterateWhile}
**************************************************************

.[perex]
La balise `{iterateWhile}` convient pour diverses astuces dans les cycles foreach.

Supposons que nous ayons la table de base de données suivante, où les articles sont divisés en catégories :

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

Bien sûr, dessiner les éléments d'une boucle foreach sous forme de liste est facile :

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

Mais que faire si vous voulez rendre chaque catégorie dans une liste séparée ? En d'autres termes, comment résoudre la tâche consistant à regrouper les éléments d'une liste linéaire dans un cycle foreach. Le résultat devrait ressembler à ceci :

```latte
<ul>
	<li>Apple</li>
	<li>Banana</li>
</ul>

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

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

Nous allons vous montrer comment cette tâche peut être résolue facilement et élégamment avec iterateWhile :

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

Alors que `{foreach}` marque la partie extérieure du cycle, c'est-à-dire le dessin des listes pour chaque catégorie, les balises `{iterateWhile}` indiquent la partie intérieure, c'est-à-dire les éléments individuels.
La condition dans la balise end indique que la répétition se poursuivra tant que l'élément actuel et le suivant appartiennent à la même catégorie (`$iterator->nextValue` est l'[élément suivant |/tags#$iterator]).

Si la condition est toujours remplie, alors tous les éléments sont dessinés dans le cycle intérieur :

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

Le résultat ressemblera à ceci :

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

A quoi sert une telle utilisation de iterateWhile ? En quoi diffère-t-elle de la solution que nous avons montrée au tout début de ce tutoriel ? La différence est que si la table est vide et ne contient pas d'éléments, elle ne rendra pas vide `<ul></ul>`.


Solution sans `{iterateWhile}` .[#toc-solution-without-iteratewhile]
--------------------------------------------------------------------

Si nous résolvions la même tâche avec des constructions complètement basiques de systèmes de templates, par exemple en Twig, Blade ou en PHP pur, la solution ressemblerait à ceci :

```latte
{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* la catégorie a changé *}

		{* nous fermons le précédent <ul>, si ce n'est pas le premier élément *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* nous allons ouvrir une nouvelle liste *}
		<ul>

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

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

{if $prevCatId !== null}
	{* on ferme la dernière liste *}
	</ul>
{/if}
```

Cependant, ce code est incompréhensible et peu intuitif. Le lien entre les balises HTML d'ouverture et de fermeture n'est pas du tout clair. Il n'est pas clair au premier coup d'œil s'il y a une erreur. Et il nécessite des variables auxiliaires comme `$prevCatId`.

En revanche, la solution avec `{iterateWhile}` est propre, claire, ne nécessite pas de variables auxiliaires et est infaillible.


Condition dans la balise de fermeture .[#toc-condition-in-the-closing-tag]
--------------------------------------------------------------------------

Si nous spécifions une condition dans la balise d'ouverture `{iterateWhile}`, le comportement change : la condition (et le passage à l'élément suivant) est exécutée au début du cycle interne, et non à la fin.
Ainsi, alors que `{iterateWhile}` sans condition est toujours entré, `{iterateWhile $cond}` n'est entré que lorsque la condition `$cond` est remplie. En même temps, l'élément suivant est écrit sur `$item`.

Ceci est utile, par exemple, dans une situation où vous souhaitez rendre le premier élément de chaque catégorie d'une manière différente, comme par exemple :

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

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

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

Modifions le code original, nous dessinons le premier élément et ensuite les éléments supplémentaires de la même catégorie dans la boucle interne `{iterateWhile}`:

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


Boucles imbriquées .[#toc-nested-loops]
---------------------------------------

Nous pouvons créer plusieurs boucles internes dans un cycle et même les imbriquer. De cette façon, on peut par exemple regrouper des sous-catégories.

Supposons qu'il y ait une autre colonne dans le tableau `subCatId` et qu'en plus de chaque catégorie se trouvant dans une colonne séparée, chaque sous-catégorie se trouve dans une colonne séparée. `<ul>`chaque sous-catégorie se trouvera dans une colonne distincte. `<ol>`:

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


Filter |batch .[#toc-filter-batch]
----------------------------------

Le regroupement d'éléments linéaires est également assuré par un filtre `batch`, en lots d'un nombre fixe d'éléments :

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

Il peut être remplacé par iterateWhile comme suit :

```latte
<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>
```

{{leftbar: /@left-menu}}

Tout ce que vous avez toujours voulu savoir sur {iterateWhile}

La balise {iterateWhile} convient pour diverses astuces dans les cycles foreach.

Supposons que nous ayons la table de base de données suivante, où les articles sont divisés en catégories :

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

Bien sûr, dessiner les éléments d'une boucle foreach sous forme de liste est facile :

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

Mais que faire si vous voulez rendre chaque catégorie dans une liste séparée ? En d'autres termes, comment résoudre la tâche consistant à regrouper les éléments d'une liste linéaire dans un cycle foreach. Le résultat devrait ressembler à ceci :

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

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

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

Nous allons vous montrer comment cette tâche peut être résolue facilement et élégamment avec iterateWhile :

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

Alors que {foreach} marque la partie extérieure du cycle, c'est-à-dire le dessin des listes pour chaque catégorie, les balises {iterateWhile} indiquent la partie intérieure, c'est-à-dire les éléments individuels. La condition dans la balise end indique que la répétition se poursuivra tant que l'élément actuel et le suivant appartiennent à la même catégorie ($iterator->nextValue est l'élément suivant).

Si la condition est toujours remplie, alors tous les éléments sont dessinés dans le cycle intérieur :

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

Le résultat ressemblera à ceci :

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

A quoi sert une telle utilisation de iterateWhile ? En quoi diffère-t-elle de la solution que nous avons montrée au tout début de ce tutoriel ? La différence est que si la table est vide et ne contient pas d'éléments, elle ne rendra pas vide <ul></ul>.

Solution sans {iterateWhile}

Si nous résolvions la même tâche avec des constructions complètement basiques de systèmes de templates, par exemple en Twig, Blade ou en PHP pur, la solution ressemblerait à ceci :

{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* la catégorie a changé *}

		{* nous fermons le précédent <ul>, si ce n'est pas le premier élément *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* nous allons ouvrir une nouvelle liste *}
		<ul>

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

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

{if $prevCatId !== null}
	{* on ferme la dernière liste *}
	</ul>
{/if}

Cependant, ce code est incompréhensible et peu intuitif. Le lien entre les balises HTML d'ouverture et de fermeture n'est pas du tout clair. Il n'est pas clair au premier coup d'œil s'il y a une erreur. Et il nécessite des variables auxiliaires comme $prevCatId.

En revanche, la solution avec {iterateWhile} est propre, claire, ne nécessite pas de variables auxiliaires et est infaillible.

Condition dans la balise de fermeture

Si nous spécifions une condition dans la balise d'ouverture {iterateWhile}, le comportement change : la condition (et le passage à l'élément suivant) est exécutée au début du cycle interne, et non à la fin. Ainsi, alors que {iterateWhile} sans condition est toujours entré, {iterateWhile $cond} n'est entré que lorsque la condition $cond est remplie. En même temps, l'élément suivant est écrit sur $item.

Ceci est utile, par exemple, dans une situation où vous souhaitez rendre le premier élément de chaque catégorie d'une manière différente, comme par exemple :

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

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

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

Modifions le code original, nous dessinons le premier élément et ensuite les éléments supplémentaires de la même catégorie dans la boucle interne {iterateWhile}:

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

Boucles imbriquées

Nous pouvons créer plusieurs boucles internes dans un cycle et même les imbriquer. De cette façon, on peut par exemple regrouper des sous-catégories.

Supposons qu'il y ait une autre colonne dans le tableau subCatId et qu'en plus de chaque catégorie se trouvant dans une colonne séparée, chaque sous-catégorie se trouve dans une colonne séparée. <ul>chaque sous-catégorie se trouvera dans une colonne distincte. <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}

Filter |batch

Le regroupement d'éléments linéaires est également assuré par un filtre batch, en lots d'un nombre fixe d'éléments :

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

Il peut être remplacé par iterateWhile comme suit :

<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>