f:form-ViewHelpers

Allgemeines zu den Formelementen

Evtl. fragt Ihr Euch warum für jedes Formelement ein eigener ViewHelper programmiert wurde? Na ich denke, weil man sich damit erstens Zeit spart (Dank der Property-Eigenschaft) und zweitens Fehler reduzieren kann (Dank der Property-Eigenschaft). Spielen wir mal ein paar Beispiele durch:

Beispiel: Not Extbase/Fluid like

Wenn wir ohne TYPO3 bzw. ohne CMS ein einfaches Formular programmieren würden, dann könnte ein Eingabefeld in etwa so aussehen:

<input type="text" name="autoMarke" />

Innerhalb der Programmierung würden wir dann mit $_POST oder ähnlichem wieder auf diesen Wert nach dem Absenden zugreifen. Im Falle von TYPO3 wird mit der TYPO3-API auf die Formulardaten zugegriffen:

$marke = t3lib_div::_GP('autoMarke');

Aber egal wie, um die Sicherheit habt Ihr Euch selbst zu kümmern. Ihr müsst selbst prüfen, ob es sich um einen Text/String/Int/Bool oder was auch immer handelt.

Beispiel: Formulare mit Actions

Wer jetzt schonmal Extensions auf piBase-Basis programmiert hat erinnert sich evtl. noch an $this->piVars. In diesem Array waren alle GET/POST-Variablen enthalten, die nur für diese eine Extension gültig waren. Bei Extbase geht dieser Schritt noch ein bisschen weiter. Hier wird nun sogar zwischen den jeweiligen Plugins unterschieden. Folgendes Formularfeld als Beispiel:

<input type="text" name="tx_sffluid_sffluid[autoMarke]" />

Das name-Attribut gibt an für welche Extension (das erste sffluid) bzw. welches Plugin (das zweite sffluid) der enthaltene Wert verfügbar gemacht werden soll. Wenn wir dieses Formular nun abschicken, können wir mit allen Actions der Extension sffluid in dem Plugin sffluid auf dieses Formularfeld zugreifen:

/**
 * @param string $autoMarke
 * @return void
 */
public function createAction($autoMarke) {
t3lib_utility_Debug::debug($autoMarke, 'marke');
die('Ende');
}

Kein $_POST, kein $_GET und keine Verwendung irgendwelcher TYPO3-API. Das macht alles Extbase für uns, solange wir uns an die exakte Namensgebung halten. Ein Rechtschreibfehler im name-Attribut des Formularfeldes resultiert hier in diesem Beispiel in eine leere Ausga

Beispiel: Formulare mit Extbase (Array)

Auch wenn das Actionbeispiel funktioniert, so setzt Extbase noch einen oben drauf. Denn was wäre, wenn wir ein großes Formular mit 30, 40 oder sogar 50 Formularfeldern hätten? Dann müssten wir auch die Action entsprechend erweitern und 50 Parameter übergeben. Das wäre eine ewig lange und vor allem unübersichtliche Action.

Bei Extbase gehört immer alles irgendwie zusammen und so wollen wir natürlich nicht nur unsere autoMarke versenden, sondern mehrere Daten, die ein Auto betreffen. Also erstellt man sich ein Model, das z.B. "Auto" heißt und vergibt diesem unteranderem die Eigenschaft "marke" und schon könnte unser Formular folgendermaßen aussehen:

<input type="text" name="tx_sffluid_sffluid[auto][marke]">
<input type="text" name="tx_sffluid_sffluid[auto][farbe]">
<input type="text" name="tx_sffluid_sffluid[auto][ps]">

Und der Aufruf innerhalb der Action wird auch eine ganze Ecke kürzer:

/**
 * @param $auto
 * @return void
 */
public function createAction(array $auto) {
t3lib_utility_Debug::debug($auto, 'auto');
die('Ende');
}

Beispiel: Formulare mit Extbase (Objekt)

Wenn wir mit Objekten arbeiten, können wir uns einiges an Schreibarbeit sparen und die Sache mit den name-Attributen ist eine ganze Ecke weniger fehleranfällig. In unserem f:form-ViewHelper geben wir mit Hilfe des name-Attributes an, wie unser Objekt heißen soll. Bei diesem Objektnamen handelt es sich später um den Variablennamen (Parameter) der an die aufgerufene Action übergeben wird. Also bitte keine Leerzeichen oder sowas.

Wenn wir unserem Template ein Objekt übergeben haben ($this->assign()), dann können wir dieses Objekt mit dem f:form-ViewHelper verknüpfen. In diesem besonderen Fall stehen uns nun die property-Attribute zur Verfügung, die uns die Arbeit abnehmen das name-Attribut richtig zu setzen. Das Ergebnis ist das Gleiche wie aus dem oberen Array-Beispi

<f:form action="create" name="auto" object="{auto}">
<f:form.textfield property="marke" /><br />
<f:form.textfield property="farbe" /><br /> <f:form.textfield property="ps" /><br /> <f:form.submit value="Erstellen" />
</f:form>

Nach Absenden des Formulares erhalten wir auch hier erstmal wieder ein Array, wenn uns Extbase nicht zuvorkommen würde:

/**
 * @param $auto
 * @return void
 */
public function createAction(Tx_Sffluid_Domain_Model_Auto $auto) {
t3lib_utility_Debug::debug($auto->getMarke(), 'Marke');
t3lib_utility_Debug::debug($auto->getFarbe(), 'Farbe');
die('Ende');
}

Extbase ließt VOR dem Aufruf einer jeden Action die Parameter der aufzurufenden Action aus und kann somit den Typen der Variable ermitteln. Im vorherigen Beispiel hatte ich "array" vor den Parameter geschrieben und konnte innerhalb der Action wie mit einem Array auf die Inhalte zugreifen. Hier in diesem Beispiel steht nun aber der Typ Tx_Sffluid_Domain_Model_Auto vor dem Parameter. Extbase prüft nun, ob es eine Klasse (Model) mit diesem Typennamen gibt und versucht nun alle Werte des Arrays in das Model zu schreiben. Dabei muss der Schlüsselname eines Arraywertes mit dem Eigenschaftennamen aus dem Model übereinstimmen. Wenn nicht, dann wird der Wert nicht mit in das Model aufgenommen.

f:form.checkbox

Parameter

  • checked: Wenn aktiviert, dann gilt diese Checkbox als markiert.
  • property: Wenn angegeben, dann bezieht sich diese Checkbox die benötigten Daten aus einer Objekteingenschaft

Dieser ViewHelper erzeugt eine Checkbox. Derzeit sind Checkboxen innerhalb von Fluid/Extbase noch eine echte Katastrophe. Es hat mich viel Zeit gekostet eine Checkbox überhaupt ans Laufen zu bekommen, da sie ein leeres aber vorhandenes Model erfordern, wenn sie mit Hilfe der Property-Eigenschaft an ein Objekt gebunden werden:

No value found for key "Tx_Fluid_ViewHelpers_FormViewHelper->formObject"

Um diesen Fehler weg zu bekommen, darf das Objekt nicht NULL sein. In der entsprechenden Action muss ein leeres Objekt erstellt werden. Könnte dann z.B. so aussehen (Auszug aus dem extension_builder):

/**
 * action new
 *
 * @param $newAuto
 * @dontvalidate $newAuto
 * @return void
 */
public function newAction(Tx_Sffluid_Domain_Model_Auto $newAuto = NULL) {
if ($newAuto == NULL) { // workaround for fluid bug ##5636
$newAuto = t3lib_div::makeInstance('Tx_Sffluid_Domain_Model_Auto');
}
$this->view->assign('newAuto', $newAuto);
}

Erst nach dieser Änderung erscheint überhaupt mal eine Checkbox.

Beispiel ohne property

Das Problem bei dieser Variante: Ihr müsst Euch selbst darum kümmern zu prüfen, ob die Checkbox markiert ist oder nicht. Ihr müsst die Werte schon innerhalb Eures Controllers gesetzt haben und mit Hilfe des Checked-Attributes zuweisen. Erlaubt ist nur TRUE oder FALSE bzw. 1 oder 0. Ihr müsst hier auch die leeren eckigen Klammern setzen, damit die markierten Werte später als Array (Mehrfachauswahl) übergeben werden können.

<f:form.checkbox name="myExtName[pizza][]" checked="{data.salami}" value="Salami" />
<f:form.checkbox name="myExtName[pizza][]" checked="{data.hawaii}" value="Hawaii" />
<f:form.checkbox name="myExtName[pizza][]" checked="{data.tonno}" value="Tonno" />

Beispiel mit property

Damit Ihr dem Problem von oben entgehen könnt, bindet die Checkboxen an eine Objekteigenschaft, die Ihr im f:form-ViewHelper hinterlegt habt:

<f:form.checkbox property="pizza" value="Salami" />
<f:form.checkbox property="pizza" value="Hawaii" />
<f:form.checkbox property="pizza" value="Tonno" />

Schaut schon eine ganze Ecke kürzer aus.

Beispiel Multiselect

Der extension_builder kann bis lang nur EINE Checkbox erstellen. Nämlich dann, wenn der Typ "Boolean" gewählt wurde. Wollt Ihr aber wie in den oberen Beispielen mehrere Checkboxen gleichzeitig setzen, dann müsst Ihr Euch vom Typ "Boolean" verabschieden. Mit dem alten kickstarter von TYPO3 können bis zu 10 gruppierte Checkboxen erstellt werden in dem die Checkboxen binär in der Datenbank abgespeichert wurden. Jede Stelle dieser "Zahl" spiegelte eine Checkbox wieder. 0 für deaktiviert. 1 für aktiviert. Ich will Euch heute eine Möglichkeit zeigen, wie Ihr über 10 Checkboxen abspeichern könnt.

Innerhalb des extension_builder wählt Ihr nun den Typ "Text" aus. Damit wird in der Datenbank eine Spalte erstellt, die etwas über 65.000 Zeichen abspeichern kann. Vergesst nicht über Database Analyser den neuen Typ in die Datenbank zu schreiben.

Das Formular bleibt ähnlich den oberen Beispielen:

<f:form.checkbox property="farbe" value="gelb" />&nbsp:gelb<br />
<f:form.checkbox property="farbe" value="braun" />&nbsp:braun<br /> <f:form.checkbox property="farbe" value="blau" />&nbsp:blau<br /> 

Da Ihr ein Array nicht in der Datenbank abspeichern könnt, müsst Ihr das Array, das von den Checkboxen kommt in einen String konvertieren. Das könnt Ihr zum Beispiel mit serialize() und unserialize() realisieren. Bearbeitet dazu in Eurem Model den getter und setter für die Checkboxen:

/**
 * @var string
 */
protected $farbe;
/**
 * @return array $farbe
 */
public function getFarbe() {
return unserialize($this->farbe);
}
/**
 * @param array $farbe
 * @return void
 */
public function setFarbe(array $farbe) {
$this->farbe = serialize($farbe);
}

und fügt einen Konstruktor im Model hinzu:

/**
 * initializes this object
 * 
 * @param string $marke
 * @param string $beschreibung
 * @param boolean $unfall
 * @param array $farbe
 */
public function __construct($marke = '', $beschreibung = '', $unfall = false, array $farbe = array()) {
$this->setMarke($marke);
$this->setBeschreibung($beschreibung);
$this->setUnfall($unfall);
$this->setFarbe($farbe);
}

Nur mit diesem Konstruktor wird Eure Gruppe von Checkboxen überhaupt zur Mehrfachauswahl. Schaut Euch auch den Quellcode an:

<input type="checkbox" name="tx_sffluid_sffluid[newAuto][farbe][]" value="gelb" /><br /> <input type="checkbox" name="tx_sffluid_sffluid[newAuto][farbe][]" value="braun" /><br /> <input type="checkbox" name="tx_sffluid_sffluid[newAuto][farbe][]" value="blau" /><br />

An Hand der leeren eckigen Klammern kann man schön erkennen, dass nun die Mehrfachauswahl möglich ist. Auch das Bearbeiten vorhandener Datensätze klappt mit dieser Methode problemlos.

Einziger Nachteil: Im Backend lassen sich die Checkboxen nicht mehr setzen.

f:form.errors

Veraltet. Bitte verwendet den ValidationResults-ViewHelper

f:form.hidden

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Checkbox die benötigten Daten aus einer Objekteingenschaft

Mit diesem ViewHelper erstellst Du ein verstecktes Feld. Das ist schonmal nützlich, um Datensatz-UIDs abzulegen, die der Webseitenbesucher nicht zu sehen braucht, aber für Dich wichtig sind, wenn es darum geht, die eingegebenen Daten einem Datensatz zuzuordnen, um ihn z.B. zu speichern.

Beispiel

<f:form.hidden name="myExtName[ttAddressUid]" value="15" />

oder

<f:form.hidden property="ttAddressUid" value="15" />

f:form.password

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Checkbox die benötigten Daten aus einer Objekteingenschaft

Mit diesem ViewHelper erstellst Du ein Textfeld dessen Inhalt nicht lesbar ist. Alle Zeichen werden sofort in Sternchen umgewandelt.

Beispiel

<f:form.password name="myExtName[password]" />

oder

<f:form.password property="password" />

f:form.radio

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Checkbox die benötigten Daten aus einer Objekteingenschaft

Mit diesem ViewHelper erstellst Du einen Radiobutton. Normalerweise tauchen diese immer in Gruppen auf und bieten dem Besucher an sich für EINE Möglichkeit zu entscheiden. Eine Mehrfachauswahl wie bei den Checkboxen ist hier nicht möglich.

Beispiel

<f:form.radio name="myExtName[age]" value="1-10" />
<f:form.radio name="myExtName[age]" value="11-40" />
<f:form.radio name="myExtName[age]" value="41-99" />

oder

<f:form.radio property="age" value="1-10" />
<f:form.radio property="age" value="11-40" />
<f:form.radio property="age" value="41-99" />

f:form.select

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Checkbox die benötigten Daten aus einer Objekteingenschaft
  • options: Welche Optionen sollen in der Auswahlliste erscheinen.
  • optionValueField: Wenn options Objekte enthält, dann könnt Ihr hier angeben welche Eigenschaft als zu übergebenden Wert verwendet werden soll
  • optionLabelField: Wenn options Objekte enthält, dann könnt Ihr hier angeben welche Eigenschaft als anzuzeigender Titel verwendet werden soll
  • sortByOptionLabel: Soll nach den anzuzeigendenTitel sortiert werden.
  • selectAllByDefault: Damit alle Optionen direkt vorausgewählt sind, muss das Attribut multiple und die size größer 1 gesetzt sein.

Mit diesem ViewHelper erstellst Du eine Selectbox

Beispiel

<f:form.select name="myExtName[country]" options="{data.countries}" />

oder

<f:form.select property="country" options="{data.countries}" />

f:form.submit

Dieser ViewHelper besitzt keine eigenen Parameter. Er bindet eine Schaltfläche ein die nach dem Anklicken das Formular absendet und somit die eingegebenen Formulardaten an den Server übermittelt

Beispiel

<f:form.submit value="Absenden" />

f:form.textarea

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Textarea die benötigten Daten aus einer Objekteingenschaft

Mit diesem ViewHelper erstellst Du ein Memofeld. Memofelder könnt Ihr für die Eingabe mehrzeiliger Texte verwenden. Gut geeignet z.B. für das Nachrichtenfeld im Kontaktformular.

Beispiel

<f:form.textarea name="myExtName[nachricht]" />

oder

<f:form.textarea property="nachricht" />

f:form.textbox

Veraltet. Bitte verwendet den f:form.textfield-ViewHelper

f:form.textfield

Parameter

  • property: Wenn angegeben, dann bezieht sich diese Textfeld die benötigten Daten aus einer Objekteigenschaft
  • required: Wenn aktiviert, dann handelt es sich um ein Pflichtfeld
  • type (Standard: text): Als Alternative gibt es noch email, url
  • placeholder: Ein beliebiger Text, der verschwindet, sobald in dieses Feld reingeklickt wird.

Mit diesem ViewHelper erstellst Du ein Textfeld

Beispiel

<f:form.textfield name="myExtName[strasse]" />

oder

<f:form.textfield property="strasse" />

f:form.upload

Parameter

  • property: Wenn angegeben, dann bezieht sich dieses Uploadfeld die benötigten Daten aus einer Objekteingenschaft

Mit diesem ViewHelper erstellst Du ein Uploadfeld, um Dateien an den Server zu senden.

Beispiel

<f:form.upload name="myExtName[image]" />

oder

<f:form.upload property="image" />

f:form.validationResults

Dokumentation folgt.