Was Sie schon immer über {iterateWhile} wissen wollten
Das Tag {iterateWhile}
ist für verschiedene Tricks in foreach-Zyklen geeignet.
Angenommen, wir haben die folgende Datenbanktabelle, in der die Elemente in Kategorien unterteilt sind:
id | catId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Es ist natürlich einfach, Elemente in einer foreach-Schleife als Liste zu zeichnen:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Aber was ist zu tun, wenn Sie jede Kategorie in einer separaten Liste darstellen wollen? Mit anderen Worten, wie kann man die Aufgabe lösen, Elemente aus einer linearen Liste in einem foreach-Zyklus zu gruppieren. Die Ausgabe sollte wie folgt aussehen:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Wir werden Ihnen zeigen, wie einfach und elegant die Aufgabe mit iterateWhile gelöst werden kann:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->catId === $iterator->nextValue->catId}
</ul>
{/foreach}
Während {foreach}
den äußeren Teil des Zyklus markiert, d.h. die Erstellung von Listen für jede Kategorie,
geben die Tags {iterateWhile}
den inneren Teil an, d.h. die einzelnen Elemente. Die Bedingung im End-Tag besagt, dass
die Wiederholung so lange fortgesetzt wird, wie das aktuelle und das nächste Element zur selben Kategorie gehören
($iterator->nextValue
ist das nächste Element).
Wenn die Bedingung immer erfüllt ist, werden alle Elemente im inneren Zyklus gezeichnet:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
Das Ergebnis sieht dann so aus:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>PHP</li>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Wozu ist eine solche Verwendung von iterateWhile gut? Wie unterscheidet sie sich von der Lösung, die wir ganz am Anfang des
Tutorials gezeigt haben? Der Unterschied besteht darin, dass die Tabelle, wenn sie leer ist und keine Elemente enthält, nicht
leer dargestellt wird <ul></ul>
.
Lösung ohne {iterateWhile}
Würden wir die gleiche Aufgabe mit ganz einfachen Konstruktionen von Template-Systemen lösen, zum Beispiel in Twig, Blade oder reinem PHP, würde die Lösung etwa so aussehen:
{var $prevCatId = null}
{foreach $items as $item}
{if $item->catId !== $prevCatId}
{* die Kategorie hat sich geändert *}
{* wir schließen das vorherige <ul>, wenn es nicht das erste Element ist *}
{if $prevCatId !== null}
</ul>
{/if}
{* wir öffnen eine neue Liste *}
<ul>
{do $prevCatId = $item->catId}
{/if}
<li>{$item->name}</li>
{/foreach}
{if $prevCatId !== null}
{* wir schließen die letzte Liste *}
</ul>
{/if}
Dieser Code ist jedoch unverständlich und unintuitiv. Der Zusammenhang zwischen den öffnenden und schließenden HTML-Tags ist
überhaupt nicht klar. Es ist nicht auf den ersten Blick klar, ob ein Fehler vorliegt. Und es werden Hilfsvariablen wie
$prevCatId
benötigt.
Im Gegensatz dazu ist die Lösung mit {iterateWhile}
sauber, klar, benötigt keine Hilfsvariablen und ist
narrensicher.
Bedingung im abschließenden Tag
Wenn wir im öffnenden Tag {iterateWhile}
eine Bedingung angeben, ändert sich das Verhalten: Die Bedingung (und
der Übergang zum nächsten Element) wird am Anfang des inneren Zyklus ausgeführt, nicht am Ende. Während also
{iterateWhile}
ohne Bedingung immer eingegeben wird, wird {iterateWhile $cond}
nur eingegeben, wenn die
Bedingung $cond
erfüllt ist. Gleichzeitig wird das folgende Element in $item
geschrieben.
Dies ist z. B. dann nützlich, wenn Sie das erste Element in jeder Kategorie auf unterschiedliche Weise darstellen möchten, z. B:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Ändern wir den ursprünglichen Code, zeichnen wir das erste Element und dann weitere Elemente aus derselben Kategorie in der
inneren Schleife {iterateWhile}
:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->catId === $iterator->nextValue->catId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Verschachtelte Schleifen
Wir können mehrere innere Schleifen in einem Zyklus erstellen und sie sogar verschachteln. Auf diese Weise können zum Beispiel Unterkategorien gruppiert werden.
Angenommen, es gibt eine weitere Spalte in der Tabelle subCatId
und jede Kategorie befindet sich nicht nur in
einer separaten <ul>
befindet, wird jede Unterkategorie in einer separaten <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
Die Gruppierung von linearen Elementen erfolgt ebenfalls über einen Filter batch
in Stapel mit einer festen
Anzahl von Elementen:
<ul>
{foreach ($items|batch:3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Er kann wie folgt durch iterateWhile ersetzt werden:
<ul>
{foreach $items as $item}
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>