Das TYPO3 Form-Framework bietet zahlreiche Möglichkeiten und ist gut zu erweitern. Hier stelle ich einen einfachen weg vor, wie ein individueller Finisher Einfluss auf die nachfolgenden nehmen kann.

Ein eigener Form Finisher

Im TYPO3 Form Framework (ext:form) ist ein Finisher eine Klasse, die sich um die Verarbeitung der Daten kümmert, die der Benutzer im Formular eingegeben hat. Zum Zeitpunkt, wenn der Finisher ins Spiel kommt, sind einfache Überprüfungen der Daten, wie z.B. die Validierungen der Felder, bereits abgeschlossen. Die Finisher sind nun dafür zuständig, was mit diesen Daten passieren soll.

Für viele Anwendungsfälle bringt das Form Framework bereits fertige Finisher mit. So kann z.B. eine Bestätigungs-E-Mail an den Benutzer gesendet werden, oder der Seitenbetreiber benachrichtigt oder die Daten in die Datenbank weggespeichert werden. Und auch die Bestätigungsmeldung oder die Weiterleitung auf eine andere Seite wird durch einen Finisher realisiert.

Ein schönes Konzept im Form Framework ist, dass man einzelne Finisher hintereinanderschalten kann. Man muss sich also nicht entscheiden, ob der Anwender eine E-Mail bekommt oder die Daten in die Datenbank geschrieben werden sollen. Die Finisher bleiben aus softwarearchitektonischer Sicht sehr schlank und implementieren genau das, was sie eben tun sollen - z.B. Daten an SAP weitergeben, eine API ansprechen oder was auch immer.

 

Grundsätzlich können die Finisher auch aufeinander aufbauen. So kann z.B. ein Finisher aus den Daten ein PDF erzeugen und ein weiterer Finisher dieses PDF dann als Anhang zu einer E-Mail versenden.

Die Problemstellung

Für ein Kundenprojekt sollte ich unlängst einen Finisher implementieren, der direkt als erster Finisher zum Zug kommt und die Eingaben analysiert. In diesem Beitrag soll es nun nicht darum gehen, was genau dieser Finisher tut und was nicht. Zum Problem gehört aber, dass der Finisher Einfluss auf die nachfolgenden Finisher nehmen sollte - und zwar nicht auf alle, sondern nur auf einzelne. 

Der Einfachheit halber denken wir uns einen AntiSPAM-Finisher, der nach außen hin unsichtbar sein soll. Für den Spammer sieht es also so aus, als hätten seine Versuche Erfolg, aber intern werden keine Datensätze und E-Mails erzeugt. (Ob das jetzt sonderlich sinnvoll ist, sei dahingestellt - für die Erklärung und Lösung des Problems ist das Beispiel passend). 

Workflow der Finisher

Für unsere Seite nehmen wir an, dass folgende Finisher nacheinander abgearbeitet werden: 

  1. AntiSpam-Finisher - prüft, ob die Anfragen SPAM sind
  2. SaveToDB-Finisher - speichert die Anfrage in die Datenbank
  3. EmailFinisher - sendet dem Seitenbetreiber eine E-Mail
  4. Confirmation - zeigt eine "Vielen Dank für Ihre Nachricht" - Meldung.

 

Abhängig davon, ob der AntiSpam-Finisher nun Spam erkannt hat oder nicht, sollen die Schritte 2 + 3 übersprungen werden (oder eben nicht). Schritt 4 soll auf jeden Fall ausgeführt werden. 

Und warum ist das nun schwer?

Die Finisher sind voneinander unabhängig und sie wissen nichts voneinander. Zwar können Finisher z.B. Variablen setzen, auf die andere Finiser zugreifen können - aber wir wollen ja nicht alle Standard-Finisher erweitern, nur um sie mit unserem AntiSpam-Finisher kompatibel zu machen. 

Weiterhin kann jeder Finisher die weitere Verarbeitung abbrechen. Das hat dann allerdings zur Folge, dass kein weiterer Finisher mehr ausgeführt wird. Auch nicht das, was wir wollen. 

 

In media res

Aber stürzen wir uns erstmal rein in den Code. Zunächst einmal brauchen wir einen Finisher, der unsere Logik implementiert. 

Dafür implementieren wir eine Klasse AntiSpamFinisher:

 

<?php

namespace MarcWillmann\Sitepackage\Domain\Finishers;

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;

class AntispamFinisher extends AbstractFinisher
{
    
    /**
     * @var string
     */
    protected $shortFinisherIdentifier = 'Antispam';

    /**
     * @var mixed[]
     */
    protected array $data = [];

    protected function executeInternal(): void
    {
        // do what ever the finisher has to do
    }

}

 

und machen diese im Form Framwork bekannt: 

 

TYPO3:
  CMS:
    Form:
      prototypes:
        standard:
          finishersDefinition:
            Antispam:
              implementationClassName: MarcWillmann\Sitepackage\Domain\Finishers\AntispamFinisher
              FormEngine:
                label: 'IP Block (Anti spam)'

 

Nun können wir den Finisher schon in unseren Formularen benutzen. Dafür wird der Finisher einfach in das formular.form.yaml eingefügt, z.B. so: 

 

prototypeName: standard
finishers:
  - identifier: Antispam
  - identifier: EmailToReceiver
    options:
      subject: 'neue Bestellung'
      recipientAddress: 'sales@domain.tld'
      recipientName: '1001 TYPO3-Lösungen GmbH & Co. KG'
      senderAddress: 'no-reply@domain.tld'
      senderName: '{firstname} {lastname}'
  -
    options:
      message: 'Vielen Dank für Ihre Nachricht!'
      contentElementUid: ''
    identifier: Confirmation

 

 

Des Rätsels Lösung

An dieser Stelle steckte ich dann einige Zeit fest. Ich habe einige Ansätze versucht und wieder verworfen. Aber keine Sorge, es ist viel einfacher als gedacht: 

Zunächst setzen wir in der AntiSpamFinisher-Klasse eine Finisher-Variable, abhängig davon ob wir SPAM erkannt haben oder nicht: 

 

protected function executeInternal(): void
    {
        // do what ever the finisher has to do

        if ($isSpam) {
            $this->finisherContext->getFinisherVariableProvider()->add(
                $this->shortFinisherIdentifier,
                'enabled',
                false
            );
        } else {
            $this->finisherContext->getFinisherVariableProvider()->add(
                $this->shortFinisherIdentifier,
                'enabled',
                true
             );
    }

 

das schaut auf den ersten Blick ein wenig merkwürdig aus. Aber es wird klarer mit dem kleinen Kniff, der die Lösung vervollständigt. In der formular.form.yaml: 

 

prototypeName: standard
finishers:
  - identifier: Antispam
  - identifier: EmailToReceiver
    options:
      subject: 'neue Bestellung'
      recipientAddress: 'sales@domain.tld'
      recipientName: '1001 TYPO3-Lösungen GmbH & Co. KG'
      senderAddress: 'no-reply@domain.tld'
      senderName: '{firstname} {lastname}'
      renderingOptions:
        enabled: '{Antispam.enabled}'
  -
    options:
      message: 'Vielen Dank für Ihre Nachricht!'
      contentElementUid: ''
    identifier: Confirmation

 

überlassen wir die Entscheidung, ob die einzelnen Finisher ausgeführt werden sollen oder nicht, genau dieser Variable, die wir zu Beginn setzen. Eigentlich ganz simpel, oder? :) 

Kommentare (0)

Keine Kommentare gefunden!

Neuen Kommentar schreiben