Seit kurzem haben neue Funktionen in die Template-Engine Twig Einzug gehalten. Diese verändern die Art und Weise, wie mit mehrwertigen Daten umgegangen wird, fundamental. Aus diesem Grund soll dieser Artikel beleuchten, wie man mit den neuen Möglichkeiten umgehen kann.
Was heißt deklarativ?
Bei “filter, map und reduce” handelt es sich um ein Muster (Pattern) aus der funktionalen Programmierung. Es erlaubt die Veränderung von Sequenzen (Listen, Arrays, Vektoren, usw.) mittels mehrerer Operationen und Manipulationen, an deren Ende ein Ergebnis steht, welches entweder ausgegeben oder weiter verwendet werden kann. Der Vorteil liegt hier darin, dass diese Veränderungen deklarativ angegeben werden können. Dies führt zu einer sehr prägnanten und lesbaren Syntax.
Das Muster wird zunehmend in zahlreichen Programmiersprachen umgesetzt. So existiert es beispielsweise seit langer Zeit in JavaScript und auch in Python.
Beispiele mit Xtend
Für ModuleStudio verwende ich seit vielen Jahren die Programmiersprache Xtend. Dabei handelt es sich um eine sehr mächtige Sprache 🚀, die zu Java-Quellcode kompiliert. Dort wird sehr viel mit deklarativen Ausdrücken gearbeitet, was man nach kurzer Zeit bereits nicht mehr missen möchte ❤️.
Hier einige Beispiele aus der offiziellen Doku, die die Mächtigkeit und Flexibilität der Filtermöglichkeiten anhand von Abfragen aus einer Liste von Filmen demonstrieren:
Wie lautet die Anzahl aller Actionfilme?
|
|
In welchem Jahr wurde der beste Film aus den 80ern veröffentlicht?
|
|
Diese Varianten zeigen schön die weitere Verwendung der resultierenden Liste durch eine Verkettung mehrerer Funktionen.
Wie lautet die Summe aller Votes für die zwei Top-Filme?
|
|
Zuerst werden die Filme nach Bewertung sortiert und die zwei besten ausgewählt. Von diesen wird mit der map
-Funktion eine Liste mit der jeweiligen Anzahl an Votes erstellt. Diese Liste kann anschließend durch reduce
in eine einzelne Zahl reduziert werden, wobei die Werte zusammen addiert werden.
Im ModuleStudio-Generator
Im Generator für ModuleStudio tauchen solche Ausdrücke an allen Ecken und Winkeln auf. Hier noch ein paar Beispiele aus der Praxis.
Ein PHP-Array mit Entity-Namen erzeugen
Gewünscht ist die Ausgabe eines Arrays, welches die Namen aller Entitäten enthält, auf die eine bestimmte Bedingung zutrifft - im folgenden Fall sind das die Entitäten, welche eine display
-Aktion besitzen.
|
|
Zunächst wird mit getAllEntities
eine Liste aller Entitäten selektiert; anschließend wird diese via filter
weiter eingegrenzt (hasDisplayAction
ist hier übrigens eine Funktion, welche implizit die jeweilige Entität als Argument erhält); mit der map
-Funktion wird aus der Liste der Entitäten eine Liste derer Namen; und schließlich werden diese mit join
zu einem String zusammengefügt.
Prüfen, ob ein Uploadfeld mit bestimmtem Namensschema existiert
Der folgende Ausdruck liefert einen bool’schen Rückgabewert darüber, ob es mindestens eine Entität gibt, die ein Uploadfeld enthält, dessen namingScheme
-Eigenschaft einen gegebenen Wert hat.
|
|
Der Ausdruck entities.map[fields]
sorgt dafür, dass die Liste von Entitäten umgewandelt wird in eine Liste der Felder dieser Entitäten. Mittels .flatten
wird diese danach verflacht, so dass eine Liste von Feldern entsteht. Anschließend wird auf die gewünschte Art des Feldes sowie auf die Bedingung gefiltert. Durch ! ... empty
wird auf die Existenz mindestens eines Eintrags geprüft.
Und jetzt zu Twig
Im Folgenden wird die Syntax in Twig-Templates anhand von Beispielen aus dem Twig-Handbuch und einem Blogartikel auf symfony.com dargestellt und erklärt. Die Funktionsweise läuft ziemlich gleich wie in Xtend (und in diversen anderen Sprachen). Die Syntax unterscheidet sich geringfügig: die “innere” Funktion wird hier nicht innerhalb von Klammern angegeben, sondern in Form einer “arrow-Funktion” notiert. Diese arrow-Funktion hat Zugriff auf den aktuellen Kontext.
filter
Der filter
-Filter entfernt alle Einträge, welche nicht einer gegebenen Bedingung entsprechen, aus einer Sequenz oder einer Map (assoziatives Array).
|
|
Kombiniert mit dem for
-Tag ergibt sich eine prägnante Schreibweise, um nur über bestimmte Einträge zu iterieren:
|
|
Noch ein Beispiel für eine Schleife:
|
|
Und nun ein Beispiel mit einer Map:
|
|
Die arrow-Funktion erhält auch den Schlüssel als zweites Argument:
|
|
Abschließend ein Beispiel mit einem etwas komplexeren Filterausdruck:
|
|
map
Der map
-Filter wendet eine Funktion auf die Elemente einer Sequenz oder einer Map an..
|
|
Die arrow-Funktion erhält auch den Schlüssel als zweites Argument:
|
|
reduce
Der reduce
-Filter reduziert eine Sequenz oder eine Map iterativ auf einen einzelnen Wert. Die arrow-Funktion erhält in diesem Fall den Rückgabewert aus der vorherigen Iteration sowie den aktuellen (nächsten) Wert aus der gegebenen Liste.
|
|
Es ist optional möglich, einen Anfangswert anzugeben:
|
|
Das folgende Beispiel ermittelt die Gesamtanzahl aller Produkte in einem Warenkorb:
|
|
column
Der column
-Filter liefert die Werte einer einzelnen Spalte (sprich eines Feldes) aus dem gegebenen Array. Dies entspricht der flatten
-Funktion in Xtend.
|
|
Ressourcen zum Nachlesen in der Übersicht
- Blogartikel auf symfony.com
- Twig-Handbuch: filter-Filter
- Twig-Handbuch: map-Filter
- Twig-Handbuch: reduce-Filter
- Twig-Handbuch: column-Filter
Anwendung für Zikula und ModuleStudio
Wir werden die neuen Funktionen auf jeden Fall sowohl im Zikula Core als auch im Modul-Generator einsetzen. Dies wird aber nicht mehr für Zikula 2.x passieren, sondern bleibt der neuen Linie von Zikula Core 3.x vorbehalten.