TYPO3 / Extbase: Dynamische Validierung von Models je nach Formularauswahl

In diesem Artikel möchte ich auf die dynamische Validierung von Models bei deren Erstellung (createAction) eingehen. Das ganze ist mit ein wenig mehr Aufwand verbunden als man vielleicht meinen möchte, allerdings kann man sich sehr schnell daran gewöhnen.

Hinweis: Die in diesem Artikel behandelten Funktionen betreffen auch die Deaktivierung der Validierung von Unterobjekten, worüber ihr hier einen spezialisierten Artikel findet.

Wird ein Model über das Frontend in einer newAction erstellt, so erfolgt die Validierung anhand der PHP Annotations im jeweiligen Model (typo3conf/ext/extension/Classes/Model/Beispiel.php), zum Beispiel so:

 /**
  * perName
  *
  * @var string
  * @validate NotEmpty
  */
  protected $perName = '';

Über das „@validate NotEmpty“ wird sichergestellt, dass dieser Wert nicht leer sein darf.

Soweit so gut. In einem konkreten Fall habe ich eine Extension für Seminare geschrieben, für die sich Benutzer anmelden können. Die Anmeldung erfolgt über ein Formular das mit Extbase und Fluid realisiert wurde und somit liegt auch die Validierung des Formulars auf seiten von Extbase.
In dem Formular kann die Person per Radiobutton angeben, ob die Person selbst oder ein Träger der Rechnungsempfänger ist und je nach Auswahl werden entweder weitere Kontaktdaten von der Person oder vom Träger zu Pflichtfeldern.

Problem:

Innerhalb des Models kann man nicht einfach mal dynamisch Annotations wegnehmen oder hinzufügen (gut vielleicht kann mans schon aber OMG). Des Weiteren wird die createAction gar nicht erst aufgerufen sobald es einen Fehler gibt, es stellt sich also die Frage wo man die dynamische Validierung einbaut.

Lösung:

Die Lösung für die Conditional Validierung in Extbase habe ich auf jweiland.net gefunden und diese meinen Anforderungen angepasst und auf Namespaces aktualisiert. Bei jweiland verändert sich die Validierung je nach Typoscript Einstellung und bei mir je nach Auswahl in einem Formular. Ansonsten bleibt das Prinzip das Gleiche:
Die Validierung kann in einer entsprechenden initializeCreateAction verändert werden. Dafür ist es jedoch nötig, dass man für jede unterschiedliche Validierung ein neues Model anlegt, in dem sich lediglich die Annotations unterscheiden (ansonsten kann es vom Original erben). So gibt es bei mir nun ein Model „Anmeldung“ mit den Personenangaben als Pflichtfeldern und ein Model „AnmeldungWmt“ mit den Trägerangaben als Pflichtfeldern (WMT = With Mandatory Traeger).

Die Validierung kann nun innerhalb der initializeCreateAction im Controller meiner Anmeldungen verändert werden:

/**
 * Dynamic validation of registrations
 * @return void
 */
public function initializeCreateAction() {
    if ($this->arguments->hasArgument('newAnmeldung')) {
        
        // Workaround um an die Auswahl des Users heranzukommen
        $postData = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('tx_tgmseminare_main');
        
        if($postData['newAnmeldung']['rechEmpf']=='Traeger') {
            
            /** @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver */
            $validatorResolver = $this->objectManager->get('\TYPO3\CMS\Extbase\Validation\ValidatorResolver');
            $traegerValidator = $validatorResolver->getBaseValidatorConjunction('\TGM\TgmSeminare\Domain\Model\AnmeldungWmt');

            /** @var \TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator */
            $conjunctionValidator = $this->arguments->getArgument('newAnmeldung')->getValidator();

            // Alle alten Validatoren entfernen
            foreach ($conjunctionValidator->getValidators() as $validator) {
                $conjunctionValidator->removeValidator($validator);
            }
            

            // Validatoren des Models AnmeldungWmt hinzufuegen
            $conjunctionValidator->addValidator($traegerValidator);
        }
    }
}

Und fertig ist die dynamische Validierung.

Eine Sache findet ich persönlich jedoch nicht nicht perfekt und zwar habe ich diese mal in den Zeilen 8-11 hervorgehoben. Und zwar kam ich in der initializeCreateAction nicht per $this->arguments->getArgument(’newAnmeldung‘) an die Auswahl des Users bezüglich des Rechnungsempfängers geschweige denn anderen Formularfeldern. Zwar erhielt ich ein Komplexes  „Argument“ Objekt doch konnte ich darin nirgendwo die eingetragenen Daten wiederfinden und lese diese stattdessen unschön per _GP() aus. Hat einer von euch eine Idee wie ich schöner an die Daten rankomme?

7 Kommentare

  • Kevin Ditscheid

    Hallo,

    zu dem Problem aus Zeile 8-11:

    Man kann sich die Daten über $this->request->getArgument(’newAnmeldung‘) beschaffen. hierzu ist wichtig vorher auch mit $this->request->hasArgument(’newAnmeldung‘) zu prüfen, ob die Variable auch tatsächlich übertragen wurde, ansonsten gibt es eine Exception. In $this->arguments befindet sich unter anderem die PropertyMapper Konfiguration, über die man die Validierung im Prinzip auch hätte machen können.

    Grüße aus Köln

    Kevin Ditscheid

  • Pingback: TYPO3 / Extbase: Validierung von Unterobjekten deaktivieren | Web Dev Log

  • Danke für das Snippet. Das ist nicht sehr gut von Extbase gelöst: Was, wenn ich drei Bedingungen habe, die dann bestimmen, ob ein Feld ein Pflichtfeld ist? Dann muss ich 9 Models anlegen?

    Kleiner Fehler: Es muss so lauten:

    $validatorResolver = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Validation\\ValidatorResolver');
    
  • Gibt es denn Alternativen zu der vorgestellten Lösung? Wie Sven schon geschrieben hat, ist diese Lösung bei mehreren Bedingungen recht inakzeptabel.

  • Super Sache! Ich hatte zwar so meine Probleme mit der Einbindung es aber dann mit Verständnis tatsächlich geschafft einzubinden.
    Kann meinen Vorredner zustimmen, dass das einfacher sein sollte….

    Ich habe in einem separaten Model alle Variabeln deklariert. Funktioniert wunderbar 🙂

  • Das ist ein interessanter Ansatz.
    Aber warum erstellst Du nicht einen eigenen Validator für die Anmeldungen, an den Du dein Objekt übergibst?
    Alle Annotations aus dem Model werden geprüft, im Validator dann die Abhängigkeiten der dyn. Pflichtfelder.
    Das halte ich für wesentlich einfacher.

  • Christian Huppert

    Hallo,

    ich habe es so gemacht:
    [php]
    $validatorResolver = $this->objectManager->get(‚TYPO3\\CMS\\Extbase\\Validation\\ValidatorResolver‘);
    $modelValidator = $validatorResolver->getBaseValidatorConjunction(‚Vender\\ExtensionName\\Domain\\Model\\MyModel‘);

    if ($this->request->hasArgument(‚thePropertyIsMandatory‘)
    && $this->request->getArgument(‚thePropertyIsMandatory‘)
    ) {
    foreach ($modelValidator->getValidators() as $validator) {

    $validator->addPropertyValidator(‚propertyWhichIsNowMandatory‘,
    $this->objectManage->get(‚TYPO3\\CMS\\Extbase\\Validation\\Validator\\NotEmptyValidator‘)
    );
    }
    }

    [php]

Schreibe einen Kommentar zu Kevin Ditscheid Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Highlighting von Codes ist mit den Tags  [ts], [php], [html], [javascript], [xml] oder [code] möglich.