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>