Was Sie schon immer über Gruppierung wissen wollten

Wenn Sie mit Daten in Vorlagen arbeiten, haben Sie oft das Bedürfnis, diese zu gruppieren oder sie nach bestimmten Kriterien anzuzeigen. Zu diesem Zweck bietet Latte mehrere leistungsstarke Werkzeuge.

Der Filter und die Funktion |group ermöglichen eine effiziente Gruppierung von Daten nach bestimmten Kriterien, während der Filter |batch die Aufteilung von Daten in feste Chargen erleichtert und das Tag {iterateWhile} die Möglichkeit einer komplexeren Zyklussteuerung mit Bedingungen bietet. Jedes dieser Tags bietet spezifische Optionen für die Arbeit mit Daten, was sie zu unverzichtbaren Werkzeugen für die dynamische und strukturierte Anzeige von Informationen in Latte-Vorlagen macht.

Filter und Funktionen group

Stellen Sie sich eine Datenbanktabelle items mit in Kategorien unterteilten Artikeln vor:

id categoryId name
1 1 Apfel
2 1 Banane
3 2 PHP
4 3 Grün
5 3 Rot
6 3 Blau

Eine einfache Liste aller Elemente mit einer Latte-Vorlage würde wie folgt aussehen:

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

Wenn wir die Artikel jedoch nach Kategorien gruppieren wollen, müssen wir sie so unterteilen, dass jede Kategorie ihre eigene Liste hat. Das Ergebnis würde 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 kann einfach und elegant mit |group gelöst werden. Wir geben categoryId als Parameter an, was bedeutet, dass die Elemente auf der Grundlage des Werts $item->categoryId in kleinere Arrays aufgeteilt werden (wenn $item ein Array wäre, würden wir $item['categoryId']):

{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

Der Filter kann auch als Funktion in Latte verwendet werden, wodurch wir eine alternative Syntax erhalten: {foreach group($items, categoryId) ...}.

Wenn Sie Elemente nach komplexeren Kriterien gruppieren möchten, können Sie eine Funktion im Filterparameter verwenden. Eine Gruppierung nach der Länge des Namens würde zum Beispiel so aussehen:

{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
	...
{/foreach}

Es ist wichtig zu beachten, dass $categoryItems kein normales Array ist, sondern ein Objekt, das sich wie ein Iterator verhält. Um auf das erste Element in der Gruppe zuzugreifen, können Sie die first() Funktion verwenden.

Diese Flexibilität bei der Datengruppierung macht group zu einem äußerst nützlichen Werkzeug für die Darstellung von Daten in Latte-Vorlagen.

Verschachtelte Schleifen

Nehmen wir an, wir haben eine Datenbanktabelle mit einer weiteren Spalte subcategoryId, die Unterkategorien für jeden Artikel definiert. Wir möchten jede Hauptkategorie in einer separaten <ul> Liste und jede Unterkategorie in einer separaten geschachtelten <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 der Nette-Datenbank

Lassen Sie uns zeigen, wie Sie die Datengruppierung in Kombination mit Nette Database effektiv nutzen können. Nehmen wir an, wir arbeiten mit der Tabelle items aus dem ersten Beispiel, die über die Spalte categoryId mit dieser Tabelle categories verbunden ist:

categoryId name
1 Früchte
2 Sprachen
3 Farben

Wir laden die Daten aus der Tabelle items mit dem Nette Database Explorer Befehl $items = $db->table('items'). Bei der Iteration über diese Daten haben wir die Möglichkeit, nicht nur auf Attribute wie $item->name und $item->categoryId zuzugreifen, sondern dank der Verbindung mit der Tabelle categories auch auf die zugehörige Zeile in dieser Tabelle über $item->category. Diese Verbindung kann interessante Anwendungen aufweisen:

{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 verbundenen Zeile $item->category zu gruppieren, nicht nur nach der Spalte categoryId. Dadurch erhalten wir die ActiveRow der gegebenen Kategorie im variablen Schlüssel, so dass wir ihren Namen mit {$category->name} direkt anzeigen können. Dies ist ein praktisches Beispiel dafür, wie die Gruppierung die Vorlagen vereinfachen und die Datenverarbeitung erleichtern kann.

Filter |batch

Mit dem Filter können Sie eine Liste von Elementen in Gruppen mit einer bestimmten Anzahl von Elementen aufteilen. Dieser Filter ist ideal für Situationen, in denen Sie Daten in mehreren kleineren Gruppen darstellen möchten, z. B. für eine bessere Übersichtlichkeit oder visuelle Organisation auf der Seite.

Stellen Sie sich vor, Sie haben eine Liste von Elementen und möchten diese in Listen darstellen, die jeweils maximal drei Elemente enthalten. 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 unterteilt, wobei jede Gruppe ($batch) bis zu drei Einträge enthält. Jede Gruppe wird dann in einer eigenen <ul> Liste angezeigt.

Wenn die letzte Gruppe nicht genügend Elemente enthält, um die gewünschte Anzahl zu erreichen, können Sie mit dem zweiten Parameter des Filters festlegen, womit diese Gruppe ergänzt werden soll. Dies ist ideal, um Elemente ästhetisch auszurichten, bei denen eine unvollständige Reihe unordentlich aussehen könnte.

{foreach ($items|batch: 3, '—') as $batch}
	...
{/foreach}

Tag {iterateWhile}

Wir werden die gleichen Aufgaben, die wir mit dem |group Filter behandelt haben, mit dem {iterateWhile} Tag demonstrieren. Der Hauptunterschied zwischen den beiden Ansätzen besteht darin, dass group zunächst alle Eingabedaten verarbeitet und gruppiert, während {iterateWhile} den Fortschritt von Zyklen mit Bedingungen steuert, so dass die Iteration sequentiell erfolgt.

Zunächst wird mit iterateWhile eine Tabelle mit Kategorien gezeichnet:

{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 des Zyklus markiert, d. h. das Zeichnen von Listen für jede Kategorie, markiert das Tag {iterateWhile} den inneren Teil, 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 wäre, würden alle Elemente im inneren Zyklus gezeichnet werden:

{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 dient iterateWhile auf diese Weise? Wenn die Tabelle leer ist und keine Elemente enthält, wird kein leeres <ul></ul> gedruckt.

Wenn wir die Bedingung im einleitenden {iterateWhile} -Tag 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 Sie also {iterateWhile} immer ohne Bedingungen eingeben, geben Sie {iterateWhile $cond} nur ein, wenn die Bedingung $cond erfüllt ist. Und gleichzeitig wird das nächste Element in $item geschrieben.

Dies ist z. B. dann nützlich, wenn wir das erste Element in jeder Kategorie anders darstellen wollen, etwa 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 ändern den ursprünglichen Code so, dass wir zuerst das erste Element darstellen und dann im inneren Zyklus {iterateWhile} die anderen Elemente derselben Kategorie darstellen:

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

Innerhalb eines Zyklus können wir mehrere innere Schleifen erstellen und diese sogar verschachteln. Auf diese Weise können zum Beispiel Unterkategorien gruppiert werden.

Angenommen, die Tabelle hat eine weitere Spalte subcategoryId, und neben jeder Kategorie in einer separaten <ul>, jede Unterkategorie in einer 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}
Version: 3.0