Dynamische Formulare mit TYPO3 Formhandler

Heute schreibe ich darüber wie man mit Typo3 Formhandler ein dynamisches Formular erstellen kann. Ein dynamisches Formular ist für mich ein Formular, welches sich je nach Nutzer Eingabe ändert. Ich stand gerade vor der Herausforderung dieses Problem für einen Kunden zu lösen. Erstellt werden sollte ein Formular welches zum einen teilweise komplett neue Eingabefelder beinhaltet, je nach Auswahl eines vorherigen Feldes und zum zweiten auch ggf. andere Pflichtfelder. Da es sich um ein sehr umfangreiches Formular handelt sollte dieses zusätzlich noch auf mehrere Seiten aufgeteilt werden.

tl;dr

Multistep Formulare mit TYPO3 Formhandler
Dynamische Validierungsregeln mit Conditions
Dynamische Templatesuffix mit Conditions

Herausforderung

Wie eingangs bereits erwähnt gab es diverse Herausforderungen zu meistern die auch eine Lösung mit reinem PHP sehr aufwändig gemacht hätten. Als erstes wäre die Aufteilung auf mehrere Seiten. Anschließend die abhängig von eingegebenen Daten andere Validierung der Formularfelder und letztendlich auch noch die Anzeige weiterer Eingabefelder je nach eingegebenen Daten.

Lösung

Im Nachhinein ist die Lösung mit TYPO3 Formhandler gar nicht so schwierig wie anfangs befürchtet. Man kann in der Formulardefinition mit Bedingungen arbeiten und auch das anzeigen weiterer Formularfelder je nach eingegebenen Daten stellt kein Hexenwerk dar. Hierfür gibt es auch bereits vorbereitete Konfigurationsmöglichkeiten.

Vorgehen

Mein Vorgehen zur Lösung dieses Problems. Ich habe das Formular erst einmal komplett aufgebaut ohne Validierungsregeln und Finisher. Dabei habe ich mich an die im Artikel Formhandler richtig konfigurieren aufgezeigten Einstellungen gehalten. Ich habe also ein Vordefiniertes Formular erstellt und dieses mit einem Master Template aufgebaut.

Der Unterschied zu dem dort beschriebenen Vorgehen ist, dass ich mehrere Schritte in dem Formular genutzt habe. Dazu habe ich neben der Datei step-1.html auch eine step-2.html und eine step-3.html. Die Typoscript Konfiguration habe ich so geändert, dass mehrere templateFile Definitionen vorliegen. Konkret sieht das wie folgt aus:

1.templateFile = TEXT
1.templateFile.value = {$formhandler.multistep-form.rootPath}/html/step-1.html
2.templateFile = TEXT
2.templateFile.value = {$formhandler.application.rootPath}/html/step-2.html
3.templateFile = TEXT
3.templateFile.value = {$formhandler.application.rootPath}/html/step-3.html

Dadurch weiß Formhandler, dass es mehrere Schritte in dem Formular gibt. Um die Speicherung der Daten in der Session etc. braucht man sich nicht weiter kümmern. Das übernimmt TYPO3 Formhandler. Eine Anforderung wurde somit jetzt bereits gelöst. Es existiert ein Formular mit mehreren Schritten.

Die nächste Anforderung die wir lösen ist es unterschiedliche Formularfelder je nach eingegebenen Daten anzuzeigen.

In den 3 HTML Dateien habe ich bisher die Mindestausprägung an Formularfeldern eingetragen. Also die Felder die alle User zu sehen bekommen. In Schritt 2 gibt es für den User die Möglichkeit das Formular zu ändern. Dazu muss dieser Daten in ein Feld eingeben und durch klick auf den Button weitere Felder hinzufügen sollen weitere Felder angezeigt werden. Dazu habe ich die Datei step-2.html so verändert, dass dort ein zweites Formular definiert wurde. Zum Vergleich einmal eine minimale Version der step-2.html

<!-- ###TEMPLATE_FORM2### -->
###master_multipart-form-start_application-form###
    ###master_section-start###
        ###master_input_field1###
        <input type="submit" ###submit_reload### class="btn btn-default tiny submit" value="###LLL:submit_expand_form###" />
        ###master_submit###
    ###master_section-end###
 
###master_form-end###
<!-- ###TEMPLATE_FORM2### -->
 
<!-- ###TEMPLATE_FORM2_expand### -->
###master_multipart-form-start_application-form###
    <input type="hidden" name="###formValuesPrefix###[expand]" value="1"/>
    ###master_section-start###
        ###master_input_field1###
        ###master_input_field2###
        ###master_submit### 
    ###master_section-end###
###master_form-end### 
<!-- ###TEMPLATE_FORM2_expand### -->

Der Schritt 2 in dem Formular beinhaltet jetzt also erst einmal ein Input Field welches hier field1 heißt. Außerdem wird ein Button angezeigt welcher dafür sorgt, dass der aktuelle Schritt neu geladen wird. Der ###submit_reload### Button wird normalerweise genutzt um Dateien hoch zu laden und das Formular genau mit den eingegebenen Daten wieder anzuzeigen. Diesen nutze ich hier einfach um das Formular abzuschicken ohne dass Validierungsregeln geprüft werden und das Formular zum nächsten Schritt weiter geht. Wenn auf diesen Button geklickt wird soll nun aber ein weiteres Formularelement angezeigt werden. In dem Fall kommt das zweite definierte Form in der oben dargestellten Datei zum Einsatz. Das Formular ist mit dem Marker ###TEMPLATE_FORM2_expand### bezeichnet. Damit TYPO3 Formhandler jetzt dieses Formular anzeigt anstelle des normale ###TEMPLATE_FORM2### muss ein bisschen TypoScript in der Konfiguration geändert werden.

plugin.Tx_Formhandler.settings.predef.multistep-form {
    # Configure different settings for Formhandler using conditions like these
    if {
        1 {
            
            conditions{
                # If the field1 is not empty and step-2-reload equals "+"
                OR1 {
                    AND1 = field1 !=
                    AND2 = step-2-reload = +
                }
                # OR the field expand equals "1"
                OR2 {
                    AND1 = expand = 1
                }
            }
            isTrue {
                # Make settings for a different step 2
                2 {
                    templateSuffix = _expand
                    validators.1.config.fieldConf {
                        field2.errorCheck.1 = required
                    }
                }
            }
        }
    }
}

In obigem TypoScript ist der Teil zu sehen, der dafür zuständig ist, dass je nach eingegebenen Daten ein anderer Formularteil ausgegeben wird. Ich habe dort ein paar Bedingungen und sollten diese zutreffen so wird der templateSuffix _expand gesetzt. Ich prüfe darauf, dass field1 Inhalt enthält und zweitens, dass das Feld step-2-reload gleich dem Wert „+“ ist. step-2-reload entspricht dem Submit Feld welches in dem Formular ###TEMPLATE_FORM2### gesetzt wurde. Sollten diese beiden Bedingungen zutreffen so nutzt Formhandler zur Darstellung das oben definierte Formular ###TEMPLATE_FORM2_expand###.
Die zweite Bedingung die ich gesetzt habe prüft auf das Feld expand. Dieses ist in der Konfiguration von ###TEMPLATE_FORM2_expand### gesetzt. Diese zweite Bedingung ist notwendig, da ansonsten nach dem Abschicken und ggf. aufgrund von Validierungsfehlern erneutem anzeigen von step-2 nicht die expand Version angezeigt würde sondern wieder nur die normale, da ja die Werte des step-2-reload nicht mehr in den Formulardaten enthalten wären.
Mit dieser Konfiguration haben wir die zweite Anforderung gelöst. Es ist möglich abhängig von den eingegebenen Daten unterschiedliche Formularelemente anzuzeigen.

Im obigen TypoScript ist bereits ein Teil der dynamischen Validierung zu sehen.

validators.1.config.fieldConf {
    field2.errorCheck.1 = required
}

Diese Validierungsregeln werden nur angewendet, sofern das expand Formular auch angezeigt wird. Andernfalls wäre es für den Besucher auch nicht möglich das Formular abzuschicken, da ein Feld validiert werden würde, welches so gar nicht vorhanden wäre.

Kommen wir generell zu den Validierungsregeln. Da wir ein Multistep Formular haben müssen wir auch die Validierungsregeln auf mehrere Schritte aufteilen. Ansonsten würde TYPO3 Formhandler alle Regeln bei jedem Schritt ausführen, was dazu führen würde, dass der Besucher der Webseite nicht zu Schritt zwei kommen würde, da Felder die entsprechenden Felder noch nicht validiert wurden.
Die Aufteilung der Regeln auf die einzelnen Schritte ist mit dem folgenden TypoScript möglich.

   # Validation Rules step1
   1.validators {
       1.class = Validator_Default
       1.config.fieldConf {
           fieldstep1.erorChedk.1 = required
       }
   }
   # Validation Rules step2 
   2.validators {
       1.class = Validator_Default
       1.config.fieldConf {
           field1.errorCheck.1 = required
       }
   }
   # Validation Rules step3
   3.validators {
       1.class = Validator_Default
       1.config.fieldConf {
           fieldstep3.errorCheck.1 = required
       }
   }

Zurück zu der Anforderung, dass Felder abhängig von eingegebenen Daten validiert werden sollen. Mithilfe von Conditions ist es ebenfalls möglich Felder abhängig von anderen eingegebene Daten um weitere Validierungsregeln zu ergänzen. Oben weiter bin ich da schon kurz drauf eingegangen. Man kann Conditions definieren und im isTrue Block nur weitere Validation Rules setzen. Dadurch haben wir auch eine Möglichkeit die Anforderung nach dynamischer Validierung von Formularfeldern abhängig von eingegebenen Daten erfüllt.

Ich hoffe dieser Artikel war verständlich für dich und konnte dir weiter helfen. Solltest du Fragen zu einem Teil haben, so hinterlasse mir einfach einen Kommentar oder nutze mein Kontaktformular. Ich freue mich über Feedback.

Fazit

Es ist mit TYPO3 Formhandler möglich dynamische Formulare zu erstellen. Nachdem man die Funktionsweise von Formhandler verstanden hat kann man damit sehr einfach sehr umfangreiche Formulare erstellen. Ein Punkt den ich mir in den nächsten Tagen noch ansehen werde ist die Nutzung von Ajax um das Formular zu validieren bzw. abzuschicken. Dieses ist mit TYPO3 Formhandler ebenfalls möglich, das habe ich jedoch noch nicht bei so einem umfangreichen Formular genutzt.

TYPO3 Formhandler Mailkonfiguration

Vielleicht habt ihr euch auch schon einmal gefragt, warum Mails welche über Formhandler verschickt werden nicht mit den normalen Mail Einstellungen von TYPO3 verschickt werden. Die Einstellungen die in dem Konfigurationsarray

$TYPO3_CONF_VARS['MAIL']

vorgenommen werden scheinen keine Wirkung zu haben.

Formhandler verschickt standardmäßig Mails mit der Klasse Mailer_HtmlMail. Diese Klasse verschickt Mails direkt mit der Mail Funktion von PHP. Möchte man E-Mails vom Webserver nun mittels SMTP verschicken, so müssen als erstes die Mail Einstellungen im Installtool oder alternativ direkt in der Datei LocalConfiguration.php geändert werden.

Nachdem diese Einstellungen geändert wurden und der Test Mail Versand erfolgreich durchgeführt wurde muss die Konfiguration des Formhandler Finisher angepasst werden.

Dazu muss dem Finisher_Mail die Konfiguration

mailer {
    class = Mailer_TYPO3Mailer
}

hinzugefügt werden. Danach werden Mails vom Formhandler über die Mailer Klasse von TYPO3 verschickt. Dadurch haben die Einstellungen im TYPO3 Installtool Wirkung.

Formhandler richtig konfigurieren

Grundkonfiguration

TYPO3 Formhandler ist eine sehr mächtige Formular Extension für TYPO3 CMS. Nahezu jeder denkbare Anwendungsfall ist damit zu lösen. Von Formularen die einfach nur per Mail abgeschickt werden zu Einträgen in die Datenbank bis hin zu Newsletteranmeldungen bei externen Dienstleistern wie z.B. Cleverreach ist alles möglich. Ich habe schon viele Formulare mit Formhandler realisiert und kenne das Problem, dass die Einstiegshürde bzw. Lernkurve sehr steil ist. Daher versuche ich euch hier eine kleine Hilfestellung zu geben. Ich werde parallel zu dem wie ich diese Anleitung schreibe das ganze an einer Installation nachvollziehen. Als TYPO3 Version nutze ich die 6.2.12. Diese habe ich installiert und noch nicht weiter konfiguriert.

Als erstes installiere ich die Extension Formhandler. Die aktuelle Version ist 2.0.1. Ich habe eine Root Seite erstellt und in dieser ein TypoScript Template angelegt.

Ich habe nur ein absolutes minimum an TypoScript verwendet. Da es sich hier nur um einen Versuchsaufbau und nicht um eine Webseite handelt die online gehen soll habe ich das TypoScript auf die folgenden Zeilen beschränkt:

page = PAGE
page.10 <  styles.content.get

Dies reicht dafür, dass die Inhalte der Spalte Normal ausgegeben werden.

In der Spalte Normal lege ich nun ein Plugin Element an. Als Plugin wähle ich „Formhandler“ aus.

Rufen wir jetzt das Frontend auf so wird einzig und alleine
An error has occurred!
ausgegeben.

Trotz der Meldung dass ein Fehler aufgetreten ist heißt dies jedoch, dass Formhandler korrekt installiert wurde und arbeitet. Es existiert nun lediglich noch ein Konfigurationsproblem welches wir im nächsten Schritt lösen werden.

Formhandler Konfiguration

Als nächstes legen wir eine Konfiguration für Formhandler an. Der Einfachheit halber wird das TypoScript erst einmal in das normale Template geschrieben. Mir ist bewusst, dass das massive Nachteile hat, für den ersten Demo Zweck reicht das aber erst einmal.
Das folgende TypoScript tragen wir ins Template ein:

plugin.Tx_Formhandler.settings.predef.first-form {

  # This is the title of the predefined form shown in the dropdown box in the plugin options.
  name = My first form
  
  # All form fields are prefixed with this values (e.g. disable-submit[name])
  formValuesPrefix = form
  
  #The "id" attribute of the form. Needed for autoDisableSubmitButton.
  formID = contact-form

  langFile.1 = TEXT
  langFile.1.value = {$formhandler.first-form.rootPath}/lang/lang.xml

  templateFile = TEXT
  templateFile.value = {$formhandler.first-form.rootPath}/html/step-1.html

  # The master template is a file containing the markup for specific field types or other sub templates (e.g. for emails). You can use these predefined markups in your HTML template for a specific form.
  masterTemplateFile = TEXT
  masterTemplateFile.value = {$formhandler.first-form.rootPath}/html/mastertemplate.html

  # In case an error occurred, all markers ###is_error_[fieldname]### are filled with the configured value of the setting "default".
  isErrorMarker {
    default = has-error
  }

  # These wraps define how an error message looks like. The message itself is set in the lang file.
  singleErrorTemplate {
    totalWrap = |
  }
  

  # This block defines the error checks performed when the user hits submit.
  validators {
    1.class = Validator_Default
    1.config.fieldConf {
      name.errorCheck.1 = required
      email.errorCheck.1 = required
      email.errorCheck.2 = email
      message.errorCheck.1 = required  
    }
  }        

  finishers {

    # Finisher_Mail sends emails to an admin and/or the user.
    1.class = Finisher_Mail
    1.config {
      checkBinaryCrLf = message
      admin {
        templateFile = TEXT
        templateFile.value = {$formhandler.first-form.rootPath}/html/email-admin.html
        sender_email = {$formhandler.first-form.email.admin.sender_email}
        to_email = {$formhandler.first-form.email.admin.to_email}
        subject = TEXT
        subject.data = LLL:{$formhandler.first-form.rootPath}/lang/lang.xml:email_admin_subject
      }
    }

    # Finisher_Redirect will redirect the user to another page after the form was submitted successfully.
    5.class = Finisher_Redirect
    5.config {
      redirectPage = {$formhandler.first-form.disable-submit.redirectPage}
    }
  }
}

 

Diese Konfiguration basiert auf einer Beispielkonfiguration von der Seite http://examples.typo3-formhandler.com/start/ diese ist eine gute Anlaufstelle für einfache Beispiele. Ebenso kann ich die Dokumentation empfehlen.

In dem obigen TypoScript wird auf Konstanten zugegriffen. Die nachfolgende Konfiguration habe ich für die Constants eingetragen:

formhandler.first-form {

  # cat=Formhandler - First Form/basic/10; type=string; label=Root path: Path where the example was saved to.
  rootPath = fileadmin/formhandler/first-form/
  
  email {
    admin {
      # cat=Formhandler - First Form/basic/20; type=string; label=Admin email sender: Email address to use as sender address for the admin email.
      sender_email = 

      # cat=Formhandler - First Form/basic/20; type=string; label=Admin email recipient: Email address of an admin to receive the contact request.
      to_email = 
    }
  }

  # cat=Formhandler - First Form/basic/40; type=string; label=Redirect Page: Page ID to redirect after successful form submission.
  redirectPage = 
  

}

Mit dem TypoScript und den Constants ist die Konfiguration für das Formular abgeschlossen.

Im TypoScript haben wir zuerst einmal den Namen sowie das formValuePrefix, also den Namespace der übertragenen Parameter definiert. Anschließend ist die Sprachdatei, das Template sowie ein Master Template angegeben. Die Mastertemplate Datei beinhaltet Schnipsel, welche in der Templatedatei verwendet werden können. Die Templatedatei gibt vor, welche Felder in dem Formular vorhanden sind.

Anschließend gibt es noch die Konfiguration für isErrorMarker. Dieser beinhaltet den Text, welcher ausgegeben wird wenn ein Validierungsfehler aufgetreten ist. In diesem Fall füllt Formhandler ein paar Platzhalter im Template die genutzt werden können. Die Konfiguration isErrorMarker.default füllt den Platzhalter ###is_error_[fieldname]###, welcher im Template verwendet werden kann.

Die Konfiguration singleErrorTemplate definiert welcher Inhalt um die Fehlermeldungen für die einzelnen Felder gewrappt werden soll.

Der nächste größere Block im TypoScript, der validators Block beschreibt welche Validatoren ausgeführt werden sollen nachdem das Formular abgeschickt wurde. In dem Beispiel sind name, email und message Pflichtfelder. Außerdem muss das email Feld eine gültige E-Mail beinhalten.

Der letzte Block im TypoScript, finishers beschreibt was passieren soll nachdem das Formular abgeschickt wurde und die Validatoren erfolgreich durchlaufen wurden. Das ausgefüllt Formular wird per E-Mail verschickt und der Besucher anschließend auf eine Danke Seite die in den Constants eingetragen wird weitergeleitet. Diese abschließenden Konfigurationen, also die Mail und die Weiterleitung auf die Danke Seite sind im Abschnitt finishers im TypoScript konfiguriert.

Wir wechseln jetzt zurück zum Plugin. Auf dem Tab Plugin ganz unten unter Predefined forms steht in der Select Box jetzt ein weitere Eintrag zur Verfügung. Wir wählen dort „My first form“ aus.

Das Template

Die Mastertemplate Datei die in dem Beispiel verwendet wird habe ich im Anhang als Download zur Verfügung gestellt.

Das Template (step-1.html) beinhaltet den folgenden Inhalt:

<!-- ###TEMPLATE_FORM1### -->
###master_multipart-form-start_contact-form###
###master_section-start###
###master_input_ajax_name###
###master_input_ajax_email###
###master_textarea_ajax_message###
###master_section-end###
###master_section-start###
###master_submit-disable###
###master_section-end###
###master_form-end###
<!-- ###TEMPLATE_FORM1### -->

dabei gibt es einige wichtige Bereiche. Das fängt mit dem Kommentar in der ersten und der letzten Zeile an. Dieser muss immer genau so sein. Der einzige Variable Teil daran ist die nummer. Diese beschreibt bei einem mehrstufigen Formular den Step des Formulares. Die zweite sowie alle weiteren Zeilen beziehen sich auf das Master Template. Es wird jeweils ein bestimmter Teil aus dem Master Template gerendert. Die Zeile zwei rendert z.B. den folgenden Bereich der Master Template Datei

class="container">
###HIDDEN_FIELDS###

Die Zeile zwei der Template Datei beinhaltet neben dem Master Template Platzhalter als Suffix noch den String _contact-form. Dieser String finden ohne den führenden Unterstrich in den Platzhalter ###fieldname### im Mastertemplate Verwendung. Man befüllt diesen Platzhalter also damit.

Neben den Dateien für das Template (step-1.html) sowie der Datei mastertemplate.html gibt es noch die Datei email-admin.html. Diese beinhaltet das Template für die Email die durch den Finisher_Mail verschickt wird. Das Template besitzt den folgenden Inhalt:

<!-- ###TEMPLATE_EMAIL_ADMIN_PLAIN### -->
###master_email-admin-start-plain###
###master_email-line-plain_name###
###master_email-line-plain_email###
###master_email-line-plain_message###
###master_email-admin-end-plain###
<!-- ###TEMPLATE_EMAIL_ADMIN_PLAIN### -->

<!-- ###TEMPLATE_EMAIL_ADMIN_HTML### -->
###master_email-admin-start-html###
###master_email-line-html_name###
###master_email-line-html_email###
###master_email-line-html_message###
###master_email-admin-end-html###
<!-- ###TEMPLATE_EMAIL_ADMIN_HTML### -->

Hier sind auch wieder die Kommentare sehr wichtig, diese legen die einzelnen Bereiche des Templates fest. Ansonsten beinhaltet das Template eine Start sowie eine abschließende Zeile. Es wird in diesem Template ebenfalls wieder auf das Master Template zugegriffen. Der Platzhalter ###master_email-line-plain_name### ist wieder zweigeteilt zu betrachten. Zum einen wäre da der erste Teil ###master_email-line-plain, welcher den Part in der Master Template Datei beschreibt. Der zweite Teil name### wird wieder als ###fieldname### in der Master Template Datei aufgelöst. In der Master Template Datei wird mit einem kleinen Trick auf den Wert des entsprechenden Formularfeldes zugegriffen.
In der Master Template Datei steht ###value_###fieldname###### . der Platzhalter ###fieldname### entspricht für das erste Feld dem Strin „name“. Somit wird in der Template Datei der Platzhalter ###value_name### ersetzt.

Die 3 Template Dateien werden in den Ordner /fileadmin/formhandler/first-form/html gespeichert.

Localization

Es gibt noch eine Sprachdatei mit folgendem Inhalt:

<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<T3locallang>
	<meta type="array">
		<description>Language labels for first-Form</description>
	</meta>
	<data type="array">
		<languageKey index="default" type="array">
			<label index="label_fieldset">First Form</label>
			<label index="name">Name:</label>
			<label index="email">E-Mail:</label>
			<label index="message">Your Message:</label>
			<label index="captcha">Captcha - SPAM Protection</label>
			<label index="submit">Send</label>
			<label index="greetings">Dear</label>
			<label index="email_user_subject">Your contact request</label>
			<label index="email_user_text">We have received your request via the contact form on www.example.org. We will process your request and get in contact with you soon.</label>
			<label index="email_user_footer">---------------------------------- This message was created automatically. --------------------------------</label>
 
			<label index="email_admin_subject">Contact request</label>
			<label index="email_admin_text">A user filled out the contact form on www.example.org.</label>
			<label index="email_admin_footer">---------------------------------- This message was created automatically. --------------------------------</label>
 
			<label index="error_name_required">Please enter your name!</label>
			<label index="error_email_required">Please enter your email address!</label>
			<label index="error_email_email">Please enter a valid email address!</label>
			<label index="error_message_required">Please enter a message!</label>
		</languageKey>
	</data>
</T3locallang>

Interessant sind an dieser Datei die letzten 4 Zeilen. Für jede Validator Konfiguration sollte ein entsprechendes label angelegt werden. Der index des Labels setzt sich dabei wie folgt zusammen. Als erstes der String error, gefolgt von dem Namen des Feldes, also z.B. name. Abschließend wird der entsprechende Validator angefügt. Als komplettes Beispiel z.B. error_name_required. Die lang.xml Datei wird in dem Ordner fileadmin/formhandler/first-form/lang/ gespeichert. Mit dieser Konfiguration sollte das Formular nun im Frontend angezeigt werden.

Fragen

Solltet ihr noch Fragen haben oder sollte das bei euch nicht funktionieren, so hinterlasst mir eine Nachricht. In einem späteren Artikel werde ich euch beschreiben wie ihr die eingegebenen Daten in der Datenbank speichert.