Alles, was Sie schon immer über Gruppierung wissen wollten
Bei der Arbeit mit Daten in Templates stoßen Sie oft auf die Notwendigkeit, diese nach bestimmten Kriterien zu gruppieren oder spezifisch anzuzeigen. Latte bietet zu diesem Zweck gleich mehrere leistungsstarke Werkzeuge.
Der Filter und die Funktion |group
ermöglichen eine effiziente Gruppierung von Daten nach einem festgelegten
Kriterium, der Filter |batch
erleichtert die Aufteilung von Daten in feste Blöcke und der Tag
{iterateWhile}
bietet die Möglichkeit einer komplexeren Steuerung des Schleifenverlaufs mit Bedingungen. Jeder
dieser Tags bietet spezifische Möglichkeiten zur Datenverarbeitung und wird so zu einem unverzichtbaren Werkzeug für die
dynamische und strukturierte Darstellung von Informationen in Latte-Templates.
Filter und Funktion group
Stellen Sie sich eine Datenbanktabelle items
mit Einträgen vor, die in Kategorien unterteilt sind:
id | categoryId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Banana |
3 | 2 | PHP |
4 | 3 | Green |
5 | 3 | Red |
6 | 3 | Blue |
Eine einfache Liste aller Einträge mit einem Latte-Template würde so aussehen:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Wenn wir jedoch möchten, dass die Einträge nach Kategorien gruppiert werden, müssen wir sie so aufteilen, dass jede Kategorie ihre eigene Liste hat. Das Ergebnis sollte dann 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>
Die Aufgabe lässt sich einfach und elegant mit |group
lösen. Als Parameter geben wir categoryId
an,
was bedeutet, dass die Einträge nach dem Wert von $item->categoryId
in kleinere Arrays aufgeteilt werden (wenn
$item
ein Array wäre, würde $item['categoryId']
verwendet):
{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
Der Filter kann in Latte auch als Funktion verwendet werden, was uns eine alternative Syntax gibt:
{foreach group($items, categoryId) ...}
.
Wenn Sie Einträge nach komplexeren Kriterien gruppieren möchten, können Sie im Filterparameter eine Funktion verwenden. Zum Beispiel würde die Gruppierung von Einträgen nach der Länge des Namens so aussehen:
{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
...
{/foreach}
Es ist wichtig zu beachten, dass $categoryItems
kein gewöhnliches Array ist, sondern ein Objekt, das sich wie ein
Iterator verhält. Um auf das erste Element der Gruppe zuzugreifen, können Sie die Funktion first()
verwenden.
Diese Flexibilität bei der Datengruppierung macht group
zu einem außergewöhnlich nützlichen Werkzeug für die
Datenpräsentation in Latte-Templates.
Verschachtelte Schleifen
Stellen wir uns vor, wir haben eine Datenbanktabelle mit einer weiteren Spalte subcategoryId
, die Unterkategorien
für einzelne Einträge definiert. Wir möchten jede Hauptkategorie in einer separaten <ul>
-Liste und jede
Unterkategorie in einer separaten verschachtelten <ol>
-Liste anzeigen:
{foreach ($items|group: categoryId) as $categoryItems}
<ul>
{foreach ($categoryItems|group: subcategoryId) as $subcategoryItems}
<ol>
{foreach $subcategoryItems as $item}
<li>{$item->name}
{/foreach}
</ol>
{/foreach}
</ul>
{/foreach}
Verbindung mit Nette Database
Zeigen wir, wie man die Datengruppierung effektiv in Kombination mit Nette Database nutzt. Angenommen, wir arbeiten mit der
Tabelle items
aus dem Einführungsbeispiel, die über die Spalte categoryId
mit dieser Tabelle
categories
verknüpft ist:
categoryId | name |
---|---|
1 | Fruits |
2 | Languages |
3 | Colors |
Die Daten aus der Tabelle items
laden wir mit dem Nette Database Explorer Befehl
$items = $db->table('items')
. Während der Iteration über diese Daten haben wir die Möglichkeit, nicht nur auf
Attribute wie $item->name
und $item->categoryId
zuzugreifen, sondern dank der Verknüpfung mit der
Tabelle categories
auch auf die zugehörige Zeile darin über $item->category
. An dieser Verknüpfung
lässt sich eine interessante Anwendung demonstrieren:
{foreach ($items|group: category) as $category => $categoryItems}
<h1>{$category->name}</h1>
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
In diesem Fall verwenden wir den Filter |group
, um nach der verknüpften Zeile $item->category
zu
gruppieren, nicht nur nach der Spalte categoryId
. Dadurch haben wir in der Schlüsselvariable $category
direkt die ActiveRow
der jeweiligen Kategorie, was uns ermöglicht, ihren Namen direkt mit
{$category->name}
auszugeben. Dies ist ein praktisches Beispiel dafür, wie Gruppierung Templates übersichtlicher
machen und die Arbeit mit Daten erleichtern kann.
Filter |batch
Der Filter ermöglicht es, eine Liste von Elementen in Gruppen mit einer vorbestimmten Anzahl von Elementen aufzuteilen. Dieser Filter ist ideal für Situationen, in denen Sie Daten in mehreren kleineren Gruppen präsentieren möchten, beispielsweise zur besseren Übersichtlichkeit oder visuellen Anordnung auf der Seite.
Stellen Sie sich vor, Sie haben eine Liste von Einträgen und möchten sie in Listen anzeigen, wobei jede Liste maximal drei
Einträge enthält. Die Verwendung des Filters |batch
ist in einem solchen Fall sehr praktisch:
<ul>
{foreach ($items|batch: 3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
In diesem Beispiel wird die Liste $items
in kleinere Gruppen aufgeteilt, wobei jede Gruppe ($batch
)
bis zu drei Einträge enthält. Jede Gruppe wird dann in einer separaten <ul>
-Liste angezeigt.
Wenn die letzte Gruppe nicht genügend Elemente enthält, um die gewünschte Anzahl zu erreichen, ermöglicht der zweite Parameter des Filters, zu definieren, womit diese Gruppe aufgefüllt wird. Dies ist ideal für die ästhetische Ausrichtung von Elementen, wo eine unvollständige Reihe unordentlich wirken könnte.
{foreach ($items|batch: 3, '—') as $batch}
...
{/foreach}
Tag {iterateWhile}
Die gleichen Aufgaben, die wir mit dem Filter |group
gelöst haben, zeigen wir mit dem Tag
{iterateWhile}
. Der Hauptunterschied zwischen den beiden Ansätzen besteht darin, dass group
zuerst alle
Eingabedaten verarbeitet und gruppiert, während {iterateWhile}
den Verlauf von Schleifen mit Bedingungen steuert,
sodass die Iteration schrittweise erfolgt.
Zuerst rendern wir die Tabelle mit Kategorien mithilfe von iterateWhile:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</ul>
{/foreach}
Während {foreach}
den äußeren Teil der Schleife markiert, also das Rendern der Listen für jede Kategorie,
markiert der Tag {iterateWhile}
den inneren Teil, also die einzelnen Einträge. Die Bedingung im End-Tag
{/iterateWhile}
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ächstes Element).
Wenn die Bedingung immer erfüllt wäre, würden alle Elemente in der inneren Schleife gerendert:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}
{/iterateWhile true}
</ul>
{/foreach}
Das Ergebnis würde so aussehen:
<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? Wenn die Tabelle leer ist und keine Elemente enthält, wird kein leeres
<ul></ul>
ausgegeben.
Wenn wir die Bedingung im öffnenden Tag {iterateWhile}
angeben, ändert sich das Verhalten: Die Bedingung (und
der Übergang zum nächsten Element) wird bereits am Anfang der inneren Schleife ausgeführt, nicht am Ende. Das heißt, während
man in {iterateWhile}
ohne Bedingung immer eintritt, tritt man in {iterateWhile $cond}
nur ein, wenn die
Bedingung $cond
erfüllt ist. Und gleichzeitig wird das nächste Element in $item
geschrieben.
Das ist zum Beispiel nützlich, wenn wir das erste Element jeder Kategorie anders rendern möchten, zum Beispiel so:
<h1>Apple</h1>
<ul>
<li>Banana</li>
</ul>
<h1>PHP</h1>
<ul>
</ul>
<h1>Green</h1>
<ul>
<li>Red</li>
<li>Blue</li>
</ul>
Wir passen den ursprünglichen Code so an, dass wir zuerst den ersten Eintrag rendern und dann in der inneren Schleife
{iterateWhile}
weitere Einträge derselben Kategorie rendern:
{foreach $items as $item}
<h1>{$item->name}</h1>
<ul>
{iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Innerhalb einer Schleife können wir mehrere innere Schleifen erstellen und sie sogar verschachteln. So könnten beispielsweise Unterkategorien usw. gruppiert werden.
Angenommen, in der Tabelle gibt es noch eine weitere Spalte subcategoryId
und neben der Tatsache, dass jede
Kategorie in einem separaten <ul>
steht, steht jede Unterkategorie in einem separaten
<ol>
:
{foreach $items as $item}
<ul>
{iterateWhile}
<ol>
{iterateWhile}
<li>{$item->name}
{/iterateWhile $item->subcategoryId === $iterator->nextValue->subcategoryId}
</ol>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</ul>
{/foreach}