TYPO3 - Form in eigener Extension verwenden

Seit dem TYPO3 der 7er Serie gibt es die TYPO3-Core Extension "Form". Das ist ein weiterer Schritt der Community in Richtung Unabhängigkeit von Third-Party Extensions für allgemeine und systemrelevante Aufgaben. Wir kennen natürlich die hervoragende Extension Powermail und deren Möglichkeiten, die wir früher sehr gerne eingesetzt haben, aber unser Grundsatz im Hinblick auf TYPO3-Versions Upgrades ist: So wenig Third-Party Extensions zu verwenden als möglich.

Die Extension ist nicht vorinstalliert, sondern muss manuell über das Modul Erweiterungen installiert werden. Ist das getan, erhält man im TYPO3-Backend in der linken Navigation unter "WEB" den zusätzlichen Navigationspunkt "Formulare". Dahinter verbirgt sich das Verwalten und Anlegen der Formulare, auf das ich hier nicht weiter eingehen möchte (s. Links unten »). Wurde ein neues Formular angelegt, wird die Konfiguration in einer YAML-Datei unter "/fileadmin/user_upload/..." gespeichert. Damit das Ganze übersichtlich bleibt, kopieren wir die YAML-Datei unserer speziellen Form in das Verzeichnis einer Extbase-Extension unter "EXT:<Extensionname>/Resources/Private/Forms/", die wir speziell für die Formulare angelgt haben. Die Konfiguration ergänzen wir dann noch durch einen neuen Finisher zum Speichern der Forminhalte in der Datenbank, damit auch später noch nachvollzogen werden kann, wer/was per E-Mail über das Formular verschickt hat. 

Verwenden eines Formulars in einer eigenen Extension.

Im speziellen Fall hatten wir die Kundenanfrage, einen Service zu programmieren, über den der Webseitenbenutzer Informationen eines Medikamentes per Formular und E-Mail abfragen kann und der Medikamenten-Name bereits vorbelegt ist. Da es sich hierbei nicht nur um ein Medikament handelte, die Medikamente zukünftig auch wechseln sollten und da bereits eine Medikamenten Datenbank mit Filter usw. existierte, wäre es sinnlos aufwändig gewesen dafür einzelne Formulare anzulegen. Da das Formular selbst vom Kunden auf einfache Art bearbeitbar und veränderbar sein sollte, schied eine Umsetzung mit einem Fluid-Partial ebenfalls aus.

Für die Form-Extension gibt es eine API/Schnittstelle, die in einer eigenen Extension verwendet werden kann. Darüber hat man die Möglichkeit, die Formulare zur Runtime zu manipulieren und die Inhalte und Felder dynamisch zu halten. Dafür haben wir eine weitere Extbase-Extension angelegt mit einem Plugin für das Frontend und einer zugehörigen Flexform für die Eingabe der Form-Parameter, wie die ID/Bezeichner der Form und die ID/Bezeichner des Feldes, das dynamisch ersetzt werden soll. Desweiteren haben wir das TCA des  bestehenden Medikamentendatensatzes um eine Checkbos erweitert, damit explizit angebebn werden kann, dass dieses Medikament im Service erscheint.

In entsprechenden Controller geht man dann folgendermaßen vor. Zunächst müssen die Namespaces der folgenden Klassen dem Controller zugefügt werden:

...
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
use TYPO3\CMS\Form\Domain\Model\FormDefinition;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
use TYPO3\CMS\Form\Controller\FormFrontendController;

use TYPO3\CMS\Extbase\Mvc\Web\Request;
use TYPO3\CMS\Extbase\Mvc\Web\Response;
use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
...

Im nächsten Schritt erzeugen wir eine Instanz des "FormPersistenceManagerInterface":

...
/**
 * @var \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface
*/
protected $formPersistenceManager;

/**
 * @param \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager
 * @internal
*/
public function injectFormPersistenceManager(\TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager) {
    $this->formPersistenceManager = $formPersistenceManager;
}
..

In der entsprechenden Controller Action werden zunächst die Konfigurationsparameter aus der FlexForm und TypoScript gespeichert:

//Settings TS, FF
$conf = array_merge([], $this->settings);

Danach werden alle angelegten Formulare aus der Datenbank geholt und das zutreffende Formular gespeichert:

//Hole alle Forms aus T3::tx_form
foreach ($this->formPersistenceManager->listForms() as $form) {
    if ($form['identifier'] === $conf['formId']) {
        $persistenceIdentifier = $form['persistenceIdentifier'];
        $conf['persistenceIdentifier'] = $persistenceIdentifier;
    }
}

Danach wird die Definition der Form gespeichert und eine neue Basis-Form erzeugt:

//Lade die Form-Definition
$formDefinition = $this->formPersistenceManager->load($persistenceIdentifier);
$formDefinition['persistenceIdentifier'] = $persistenceIdentifier;
//Lade die FormFactoryClass und bilde das Formobjekt
$arrayFormFact = $this->objectManager->get(ArrayFormFactory::class);
$fm = $arrayFormFact->build($formDefinition);

Nach dem Erzeugen der Form, wird das oder die dynamisch veränderbaren Formfeld(er) gespeichert:

//Hole das Feld für die Vorbelegung
$elem = $fm->getElementByIdentifier($conf['substField']);

Für das Rendern der Form benötigen wir eine Instanz des "FormFrontendController" und jeweils eine Instanz des Request- und Response-Objekts. Das Request-Objekt füllen wir mit den entsprechenden Parametern:

//Rendern der Form
//Instanz des Form Controller erstellen
$ffc = $this->objectManager->get(FormFrontendController::class);
//Request für Form Controller
$req = $this->objectManager->get(Request::class);
$req->setControllerName('FormFrontend');
$req->setControllerActionName('perform');
$req->setControllerExtensionName('form');
$req->setPluginName('Formframework');
$req->setControllerVendorName('TYPO3.CMS');
//Response für Form Controller
$resp = $this->objectManager->get(Response::class);
//Controller Context
$cct = $this->objectManager->get(ControllerContext::class);

Die Instanzen werden dann entsprechend zugeordnet:

//Neues Request und Response Objekt in ControllerContext schreiben
$cct->setRequest($req);
$cct->setResponse($resp);
//Request und Response in Controller schreiben
$ffc->request = $req;
$ffc->response = $resp;
//Controller Context in Controller schreiben
$ffc->controllerContext = $cct;

Zuletzt wird ein Runtime-Objekt für das Rendern der Form erstellt:

//FormRuntime für Rendern erstellen
$asr = $this->objectManager->get(FormRuntime::class, $fm, $ffc->request, $ffc->response);​​​​

Nun wurden alle benötigten Objektinstanzen und Zuordnungen erzeugt. In diesem Beispiel wurde ein Feld mit dem Namen des Medikaments vorbelegt und die einzelne Form gerendert und das Ergebnis für die Ausgabe in einem Fluid-Template gespeichert, um danach im Frontend in einem Accordion pro Form ausgegeben zu werden (s. Screen unten):

//Produkttitel in Form schreiben und rendern
$elem->setDefaultValue($title);
$conf['list'][$capital][$id]['renderedForm'] = $asr->render();​​​​​​

Beispiel-Screen: 

Natürlich sind hier, durch diese hervorragende Integration der Formularverwaltung in den TYPO3-Core, noch sehr viel mehr Möglichkeiten der Manipulation einer Form zur Laufzeit denkbar. 

Weiter führende Links:

  1. https://api.typo3.org/9.5/dir_e591a8ce426346a4ba2981b1b15f4d0c.html
  2. https://docs.typo3.org/c/typo3/cms-form/master/en-us/
  3. https://docs.typo3.org/c/typo3/cms-form/8.7/en-us/Concepts/FormManager/Index.html
  4. https://www.tritum.de/typo3-cms/typo3-extensions/typo3-form-framework/
     

Erstellt am: 18.12.2019

Vielen Dank für Ihr Interesse.
Bei Fragen zu TYPO3, Programmierung oder Upgrades stehen wir Ihnen gerne zur Verfügung. -> Kontakt