TYPO3 Extbase Validierung I – Validierung von Frontend Formularen

Jeder der bereits Erfahrung mit der TYPO3 Extbase Validierung gesammelt hat wird wissen, dass die Validierung gerade für Beginner nicht einfach ist zu implementieren. Aus diesem Grund widmet sich der folgende Artikel diesem Thema.

Was wird im Folgenden näher erläutert:

  • Funktionsweise der TYPO3 Extbase Validierung
  • Workflow der Extbase Validierung
    • Extbase Validierung mittels Annotationen im Domain Model
    • Extbase Validierung in der initialize[Name] Action()
  • Vordefinierte Extbase Validatoren
  • Ausgabe der Validierungs-Fehler über Fluid

Tipp der Authorin zu Beginn: Mir hat es enorm geholfen den Cache über den „Clear all chache“ Button im Install Tool zu löschen, wenn Änderungen nicht erschienen sind.

Funktionsweise der TYPO3 Extbase Validierung

Generelle Informationen zur TYPO3 Extbase Validierung können folgenden Link entnommen werden: https://docs.typo3.org/typo3cms/ExtbaseFluidBook/9-CrosscuttingConcerns/2-validating-domain-objects.html

Prinzipiell gibt es zwei Möglichkeiten, um ein Frontend Formular zu validieren. Entweder werden sogenannte Annotations im Domain Model hinzugefügt, oder aber es werden in der sogenannte initialize[Name]Action() Validatoren hinzugefügt. Je nachdem, wie eine Applikation aufgebaut ist, kann die Methode ausgewählt werden. Beispielsweise, wenn ich ein Formular habe, dass direkt nach dem absenden validiert werden soll, dann bietet sich die Möglichkeit an, mit Annotations zu arbeiten. Wenn hingegen, erst zu einer späteren Zeit validiert werden soll – z.B.: ein Formular kann Daten zwischenspeichern– dann wäre es besser steuerbar, wenn die Validierung über die initialize[Name]Action() nachträglich hinzugefügt wird.

Exkurs initialize[Name]Action()

Für die, die noch nie mit diesen Methoden gearbeitet haben ein kurzer Exkurs, was das Besondere daran ist. Es wird unterschieden zwischen der initializeAction() die vom ActionController bei jedem Request als erstes (!) aufgerufen wird und den initialize[Name]Action() die vor der dazugehörigen Methode aufgerufen werden.

Beispiel: Mein Formular ruft beim Absenden die Methode saveAction() auf. Diese erhält die Daten des Formulars und speichert diese in der Datenbank ab. Jetzt ist es jedoch so, dass die Daten nur abgespeichert werden sollen, wenn diese auch valide sind. Dafür wird eine Methode benötigt, die vor der saveAction() – aber nur vor saveAction() – aufgerufen werden soll und diese Validierung festlegt (wenn nicht mit Annotations gearbeitet wird). Hierfür dienen die initialize[Name]Actions(). In diesem konkreten Fall ist die Methode initializeSendAction() zu benennen.

Abstimmung mit TCA

Weiters ist neben den zwei genannten Methoden noch zu beachten, dass die Valdierungsregeln die einem Objekt hinzugefügt wurden auch mit den TCA Einstellungen („eval“) betreffend Validierung soweit wie möglich übereinstimmen. Die diversen Möglichkeiten können unter dem folgenden Link nachgelesen werden:

https://docs.typo3.org/typo3cms/TCAReference/Reference/Columns/Input/Index.html#eval

Verwendung von „eval“ im TCA

Der Workflow der TYPO3 Extbase Validierung

Workflow der TYPO3 Extbase Validierung

Die Validierung folgt einem einfachen Konzept. Der User füllt das Frontend Formular aus und drückt auf den Button „Daten übermitteln“. Dieser Button ruft die Methode sendAction() auf (1). Im Controller wird vor der sendAction die initializaSendAction() aufgerufen, wenn eine entsprechende Methode implementiert wurde (2). In diesem Beispiel wird sie aufgerufen, weil nachträglich Validierungsregeln hinzugefügt werden. Danach – und dass ist das wichtige zu wissen an den Workflow – findet die TYPO3 Extbase Validierung unsichtbar(!) statt (3). Ich persönlich nenne es „Extbase Magic“.

Diese Validierung prüft, ob die übergebenen Werte den Soll-Werten entsprechen (4). Entspricht ein Wert nicht der Vorgabe, leitet Extbase den Request nicht an die sendAction weiter, sondern ruft im ActionController die errorAction() auf (5a)

Die errorAction()

Die errorAction() ist wie der Name sagt für das Error Handling zuständig. Diese Methode klont den ursprünglichen Request, und leitet zu der zuletzt für die View verantwortlichen Methode zurück. Sprich, wenn das Formular über die indexAction() angezeigt wurde, dann wird die errorAction() auf diese Methode zurück verweisen. Das Resultat: Es wird dieselbe View nochmals angezeigt, nur mit dem Unterschied, dass die aufgetretenen Fehler mitgeliefert werden (nähere Details zur Ausgabe folgen).

Auszug errorAction(): Aufruf der Action die für die letzte View zuständig war

Treten hingegen keine Fehler bei der Validierung auf wird die sendAction() aufgerufen (5b).

Die Zwickmühle

Manchmal können Formulare auch mehrstufig aufgebaut sein. Sofern gewünscht, dass nach jedem Schritt eine Validierung stattfindet, sind nach jedem Schritt die Formulardaten zu übermitteln. Dabei muss – wenn die TYPO3 Extbase Validierung verwendet wird – folgendes beachtet werden.

Während der Konzeption scheint es logisch eine Action zu verwenden, die das Formular sowohl anzeigt als auch verarbeitet (denn im Grund werden bei den einzelnen Schritten nur Felder im Formular ausgetauscht, sonst nichts). Die Erfahrung hat jedoch gezeigt, dass es fatale Folgen hat, wenn nicht (mind.) zwei Actions hierbei verwendet werden. Der Grund dafür ist simpel:

Workflow der TYPO3 Extbase Validierung beim mehrmaligen Aufruf

Extbase ruft, wenn Validierungsfehler auftreten, die errorAction auf (5a). Diese wiederum ruft die letzte Action auf, die für die Anzeige zuständig war (6). Grundsätzlich ist das ein gutes Konzept, aber das Problem welches in diesem Fall auftreten würde ist, dass durch den Aufruf der stepAction() die Validierung nochmals stattfinden würde (2 bzw. 3). Das würde wiederum in einer Endlosschleife enden und zu folgender Fehlermeldung führen

Could not ultimately dispatch the request after 101 iterations. Most probably, a @dontvalidate annotation is missing on re-displaying a form with validation errors.

Prinzipiell besagt die Fehlermeldung, dass die Endlosschleife nicht auftreten würde, wenn die Validierung nicht stattfinden würde (dies wird erreicht indem der aufgerufenen Action die Annotation @dontvalidate bzw. @ignorevalidation, ist von der TYPO3 Version abhängig, hinzugefügt wird). In diesem Beispiel würde das jedoch nicht funktionieren, da die stepAction() prinzipiell die übergebenen Daten validieren soll. Es entsteht eine Zwickmühle.

Vermeidung der Zwickmühle

Damit die oben genannte Problematik nicht entstehen kann sind (mind.) zwei Actions zu verwenden – eine für die Anzeige der View (z.B.: stepAction()) und eine für die Verarbeitung der Daten (z.B.: changeStepAction()). Dadurch entsteht folgender Workflow:

Idealer Workflow der TYPO3 Extbase Validierung beim mehrmaligen Aufruf

Der User füllt das Formular aus und klickt auf den Button „nächster Schritt“. Der Button ruft im Controller die Methode changeStepAction() auf (1). Die übermittelten Daten werden validiert (2).

Sofern es zu einem Fehler kommt wird die errorAction aufgerufen (3a), welche die Action aufruft, die für die letzte Anzeige zuständig war – in diesem Beispiel ist das die Methode stepAction()(4). Die stepAction() hat als Annotation @ingnorevalidation im Kommentarblock eingefügt. Dadurch kann problemlos die letzte View angezeigt werden, denn es findet keine nochmalige Validierung statt. Tritt hingegen kein Fehler auf, wird nach der Validierung die Methode aufgerufen, die das Formular ursprünglich aufrufen wollte – in diesem Fall die changeStepAction() (3b). Diese wiederum verarbeitet die Daten und leitet zur nächsten Ansicht – vermutlich die die für eine Ansicht zuständig ist – weiter.

Extbase Validierung mittels Annotationen im Domain Model

Extbase bietet mittels Annotation eine einfache Möglichkeit Validierungsregeln für eine Objekteigenschaft festzulegen. Grundsätzlich können zwei „Typen“ von Validatoren unterschieden werden:

  • Einfache Validatoren
  • Validatoren mit Argumenten

Prinzipiell gibt es zwischen diesen beiden Typen keinen Unterschied, außer dass bei Validatoren mit Argumenten Soll-Werte übergeben werden müssen. Die Annotation sieht wie folgt aus:

@validate Alphanumeric
@validate StringLength(minimum = 7, maximum = 10)

Die Namen der Validatoren können der Validatorklasse bzw. dem Filenamen der Validatorklasse entnommen werden. Es ist der Name ohne das Wort „Validator“ zu verwenden

@validate Alphanumeric => AlphanumericValidator.php
@validte NotEmpty => NotEmptyValidator.php

Auch ist es möglich eigene Validatoren im Domain Model zu verwenden. Dazu muss lediglich der Pfad zum Validator angegeben werden.

@validate\Plan2net\[ExtensionName]\Domain\Validator\DetectUrlValidator
Verwendung von Annotations im Domain Model

Nähere Details zu den Argumenten – Name und Wert – müssen in der Validatorklasse nachgesehen werden. In der Klasse ist die Variable $supportedOptions zu suchen. In dieser stehen die Schlüssel, sowie die erwarteten Werte, welche in weiterer Folge von der Elternklasse AbstractValidator verwendet werden.

Verwendung von $supportedOptions

 

Extbase Validierung in der initialize[Name] Action()

Manchmal ist es notwendig, dass einem Objekt zusätzliche Validierungsregeln hinzugefügt werden. Dies kann in der initialize[Name]Action() getan werden. Primär ist in diesem Fall zu unterscheiden, ob eine Validierungsregel auf ein gesamtes Objekt angewendet wird, oder aber nur auf eine bestimmte Objekteigenschaft.

Validierung eines Objekts

Damit ein ganzes Objekt validiert werden kann wird ein sogenannter ConjunctionValidator benötigt. Dieser darf jedoch nicht neu erstellt werden, sondern muss über die übermittelten Daten geholt werden. Erst dann ist es möglich, dass über die Methode addValidtor() ein neuer Validator dem Objekt hinzugefügt wird.

Validierung eines Objekts

Validierung einer Objekteigenschaft

Damit eine Objekteigenschaft validiert werden kann wird ein sogenannter GenericObjectValidator benötigt. Dieser darf – wie der ConjunctionValidator – nicht neu erstellt werden, sondern muss über den ConjunctionValidator geholt werden. Dazu dient die Methode getValidators(). Zunächst werden über diese Methode die „Subvalidatoren“ geholt. Danach wird über diese „Subvalidatoren“ der entsprechende GenericObjectValidator geholt. Dieser wiederum ermöglicht über die Methode addPropertyValidator() einer Objekteigenschaft eine neue Validierungsregeln hinzuzufügen.

Validierung einer Objekteigenschaft

Weiters ist es auch hier möglich Validatoren mit Argumenten zu verwenden. Prinzipiell gelten dieselben Regeln, wie bei den Annotationen, außer dass in diesem Fall die Argumente als Array, bei der Erstellung des Validators, übergeben werden müssen.

Verwendung von Validatoren mit Argumenten

Vordefinierte Extbase Validatoren

Extbase bietet von Haus aus vordefinierte Validatoren an, die wie bereits gezeigt, einfach verwendet werden können. Diese sind unter dem folgenden Pfad zu finden:

/sysext/extbase/Classes/Validation/Validator/

Ausgabe der Validierungs-Fehler über Fluid

Nachdem ausführlich erklärt wurde, wie die Extbase Validierung funktioniert, ist es auch wichtig zu wissen, wie dem User die Fehler die aufgetreten sind übermittelt werden können. Dazu bietet Fluid zwei Möglichkeiten an:

Über den Viewhelper validationResults

Der Viewhelper validationResults bietet die Möglichkeit dem User mitzuteilen welche Fehler aufgetreten sind. Diese können also Infobox ausgegen werden.

Ausgabe von Validierungs-Meldungen

Über das Attribut errorClass

Neben der Option mit dem Viewhelper ist es auch möglich Fluid Form Elementen das Attribut errorClass zuzuweisen, welches automatisch möglich macht, dass das entsprechende Feld gekennzeichnet wird, wenn es fehlerhaft ausgefüllt wurde. Per Default fügt Extbase die errorClass f3-form-error hinzu. Diese kann jedoch umbenannt werden.

 

Alle Code Ausschnitte können selbstverständlich auch heruntergeladen werden: Code Ausschnitte

Anfrage
Eine kleine Rechenübung*: