Formulare nach Wunsch
Dynamische FlexForms für das TYPO3 Backend mit der Extension DynaFlex
FlexForms werden im Normalfall zur Konfiguration von PlugIns benutzt. Dies reicht in den meisten Fällen auch aus, da die Eingaben im Normalfall lediglich zur Konfiguration dienen und im Frontend nicht direkt ausgegeben werden. Es können jedoch Anwendungsfälle auftreten, in denen man dem Benutzer Konfigurationsmöglichkeiten bieten möchte, die davon abhängig sind, welche Daten bereits in der Datenbank vorhanden sind oder davon, was in einem anderen Feld des Formulars eingetragen wurde. Etwa, wenn für jede der eingerichteten Webseitensprachen eine Datei in einem PlugIn hinterlegt werden soll.
Im Rahmen der Entwicklung der "commerce"-Extension (Advanced Shop System) [1] wurden Funktionalitäten gewünscht, die sich mit den gegebenen Möglichkeiten nicht umsetzen ließen. Um die großen Mengen an Daten in komfortablen Backend-Eingabemasken verarbeiten zu können, musste ein Weg gefunden werden, die Formulare im Backend in Abhängigkeit zu vorhanden Daten anzupassen.
Ein Weg, komplexere Eingabemöglichkeiten zu realisieren, sind so genannte „user defined fields“. Diese haben jedoch den Nachteil, dass man sich selbst um die Darstellung und Validierung der Daten kümmern muss. Der elegantere Weg besteht darin, diese Arbeit TYPO3 zu überlassen und stattdessen die Eingabemasken dynamisch zu erzeugen. Man nutzt also die bereits vorhandenen Eingabemöglichkeiten von TYPO3, erzeugt sie aber nach den aktuellen Bedürfnissen der eigenen Anwendung.
Was kann DynaFlex?
Wie bereits erwähnt erzeugt DynaFlex die bekannten FlexForms zur Laufzeit. Es können also auf Basis von Daten und Bedingungen Felder erzeugt oder verändert werden. DynaFlex arbeitet, wie auch das TYPO3 Backend selbst, auf Basis eines eigenen Konfigurationsarrays (DCA: DynaFlex Configuration Array). In diesem Array kann der Programmierer angeben, nach welchen Regeln eine FlexForm verarbeitet werden soll, bevor sie an TYPO3 zur Ausgabe übergeben wird. Dieses DCA ähnelt vom Aufbau stark den bekannten Konfigurationsarrays, die bereits in TYPO3 Verwendung finden (TCA) [2]. Der Entwickler hat durch das DCA vielfältige Möglichkeiten die Struktur einer FlexForm zu beeinflussen. So kann dieser:
- Felder oder Tabulatoren (Tab-Divider) hinzufügen und entfernen
- Benutzerdefinierte Funktionen aufrufen
- Felder verschieben
DynaFlex bietet die Möglichkeit, derartige Modifikationen anhand von so genannten Quellen auszuführen. Ein Beispiel wäre das Anlegen eines Feldes für jeden Datensatz, der als Ergebnis einer Datenbankabfrage geliefert wird. Die Art dieser Quellen ist dabei nicht auf Datenbankabfragen beschränkt. Seit Version 0.2.0 (aktuell im TYPO3 Extension Repository) [3] können auch andere Felder der FlexForm oder des gesamten Formulars als Quelle dienen. Beispielsweise kann dadurch eine Anzahl von Tabulatoren eingefügt werden, abhängig davon, welche Zahl in einem anderen FlexForm-Feld eingegeben wurde.
DynaFlex bietet Conditions (Bedingungen) an. Diese Conditions dienen dazu, bestimmte Aktionen nur dann auszuführen, wenn eine definierte Bedingung erfüllt ist. Erst durch Conditions wird DynaFlex wirklich dynamisch. Eine Condition vergleicht dabei das Ergebnis einer Datenbankabfrage mit einem definierten Wert. Dabei kann der zu vergleichende Wert direkt aus der Abfrage kommen, also etwa ein COUNT(*) oder ein einfaches SELECT, welche mehrere Datensätze liefert, oder aber ein Feld innerhalb einer FlexForm abfragen. Das Ergebnis der Abfrage wird daraufhin mit dem vorgegebenen Wert verglichen. Für diesen Vergleich stehen zur Zeit vier Methoden zur Verfügung:
- hasValues (Hat das Ergebnis einen Wert? Sinnvoll bei Abfrage von Feldinhalten.)
- isEqual (Ist gleich der Vorgabe)
- isGreater (Ist größer als die Vorgabe)
- isLess (Ist kleiner als die Vorgabe).
Wie arbeitet DynaFlex?
Wie bereits erwähnt, erstellt DynaFlex eine XML-Datenstruktur, die eine FlexForm beschreibt. Das genaue Format für das FlexForm-XML kann in den „TYPO3 Core APIs“ nachgelesen werden. Die XML-Strukturen für FlexForms werden im Table Configuration Array (TCA) definiert. Sie können dort direkt eingetragen sein oder werden aus externen Dateien nachgeladen. Es ist im Kern des Systems jedoch nicht vorgesehen, vor dem Anzeigen (rendern) der Backend-Formulare in irgendeiner Form einzugreifen, etwa um die Struktur des Formulars zu ändern.
Um ein dynamisches Ändern der Formularstrukturen zu ermöglichen, musste zunächst ein Weg gefunden werden, ohne Veränderung des Kerns vor dem Rendern einzugreifen. Der bisher einzige Lösungsweg liegt darin, DynaFlex direkt im TCA aufzurufen. Dieses Vorgehen wird durch die Arbeitsweise des Backends möglich. Die Dateien, welche die Definition des Backends für eine bestimmte Extension enthalten, werden immer vor der Verarbeitung des Formulars aufgerufen. Man kann dort beliebigen Code unterbringen, der dann entsprechend ausgeführt wird. Extension-Programmierer kennen diese Datei als „tca.php“ im Hauptverzeichnis ihrer Extensions. Ein konkretes Beispiel, wie DynaFlex aufgerufen werden muss und was dabei zu beachten ist, kann im Handbuch zu DynaFlex [4] nachgelesen werden.
Ein Beispiel
Anhand eines kleinen Beispiels sollen nun einige Grundlagen von DynaFlex erklärt werden. Dieses Beispiel ist bewusst sehr einfach und übersichtlich gehalten: Es soll für jeden Backendbenutzer ein einfaches Eingabefeld in eine FlexForm eingefügt werden. Dieses Beispiel hat keinen praktischen Zweck - das Vermitteln des grundsätzlichen Vorgehens steht im Vordergrund. Als Ausgangsbasis dient ein einfaches Inhaltselement mit einer FlexForm. Diese FlexForm hat dabei am Anfang keine definierten Felder, ist also leer. Im ersten Schritt erstellt man zunächst eine Konfiguration für DynaFlex. Diese soll alle Einträge aus der Tabelle "be_users" holen und für jedes dieser Felder ein einfaches Eingabefeld erzeugen.
$dynaFlexConf = array (
array (
'path' => 'tx_myext_data/columns/ff_field/config/ds/default',
'modifications' => array (
array (
'method' => 'add',
'path' => 'ROOT/el',
'type' => 'fields',
'source' => 'db',
'source_config' => array (
'table' => 'be_users',
'select' => '*',
'where' => '1',
),
'field_config' => array (
'name' => 'beuser_###uid###',
'label' => '###username###',
'config' => array (
'type' => 'input',
),
),
),
),
),
);
Listing 1
Der Pfad („path“) zeigt auf den Bereich im TCA, der bearbeitet werden soll. „ff_field“ ist das Feld, für welches die Datenstruktur definiert wurde. „config/ds/default“ ist der Pfad zur XML-Struktur. Im Bereich „modifications“ werden alle Modifikationen definiert, die für den oben angegebenen Pfad ausgeführt werden sollen. Es können grundsätzlich beliebig viele Modifikationen für einen Pfad angegeben werden, die nacheinander verarbeitet werden.
Zu jeder Modifikation gehört die Methode („method“). Sie gibt an, was generell passieren soll. Der Typ der Methode („type“) definiert näher, was passieren soll. Es gibt beispielsweise für die Methode „add" die Typen „field“, „fields“ oder „sheet“. Je nach Typ stehen weitere Optionen zur Verfügung.
Im Beispiel folgt nun die Konfiguration der Quelle („source“, „source_config“). Wie am Anfang definiert, werden die Daten für dieses Beispiel aus der Datenbank („source“) geholt. In „source_config“ werden im Falle von „db“ die Daten für die SQL-Abfrage eingetragen. Die Konfiguration der Felder erfolgt in „field_config“. Diese Konfiguration wird für jeden Datensatz, der als Ergebnis der oben konfigurierten Datenbankabfrage geliefert wird, genau einmal ausgewertet. In diesem Fall ist nur ein Feld definiert. Es ist aber möglich, ähnlich wie bei den Modifikationen, innerhalb von „field_config“ einzelne Arrays anzulegen, die alle eine Konfiguration für ein einzelnes Feld enthalten. Dadurch können für einen Datensatz auch mehrere Felder erzeugt werden.
Die Konfiguration der Felder erfolgt sehr ähnlich dem TCA. Es muss ein Name („name“), eine Beschriftung („label“) und die eigentliche Konfiguration angegeben werden. Es ist möglich, in einigen Feldern Platzhalter einzutragen. Diese Platzhalter werden mit den Feldinhalten der Quelle ersetzt. Im Beispiel wird der Platzhalter "###uid###" im Namen des Feldes durch das Feld "uid" im Datensatz der Datenbankabfrage ersetzt. Gleiches gilt auch für das Label. Die Konfiguration für DynaFlex ist damit abgeschlossen. Der Aufruf von DynaFlex erfolgt direkt im TCA der Extension (tca.php).
if (
$_REQUEST['edit']['tx_myext_data'] &&
!isset($_REQUEST['data']) &&
t3lib_extMgm::isLoaded('dynaflex')) {
require_once(t3lib_extMgm::extPath('dynaflex') .'class.dynaflex.php');
$dynaflex = t3lib_div::makeInstance('dynaflex');
$dynaflex->init($TCA, $dynaFlexConf);
$TCA = $dynaflex->getDynamicTCA();
}
Listing 2
Zunächst wird geprüft, ob die richtige Tabelle („tx_myext_data“) bearbeitet wird. Es wird geprüft, ob Daten übergeben wurden und ob DynaFlex installiert ist. In den nächsten drei Zeilen wird zunächst die Quelldatei für DynaFlex eingebunden und dann eine Instanz von DynaFlex erzeugt. In diesem Schritt wird DynaFlex das TCA und das DCA übergeben. Im letzten Schritt wird mit dem Aufruf „$TCA = $dynaflex->getDynamicTCA();“ DynaFlex gestartet und das modifizierte TCA ermittelt. Damit sind alle nötigen Schritte getan und in Zukunft werden alle Datensätze der Tabelle „tx_myext_data“ entsprechend durch DynaFlex vorverarbeitet. Das Ergebnis könnte etwa so aussehen:
DynaFlex kann wesentlich mehr, als dieses Beispiel zu zeigen vermag. Es sei daher an dieser Stelle nochmals auf das Handbuch hingewiesen, das alle Möglichkeiten der Konfiguration erläutert.
Aussichten und Perspektiven
Zurzeit wird daran gearbeitet, den Aufruf der Extension eleganter und weniger fehleranfällig zu gestalten. Es ist momentan beispielsweise nötig, DynaFlex per "Hook" aufzurufen, wenn Felder innerhalb einer FlexForm als Quelle benutzt werden. Wird DynaFlex nur über das TCA aufgerufen, muss der Benutzer zweimal speichern damit die nötigen Daten in der Datenbank verfügbar sind. Ein weiterer Punkt für Verbesserungen wäre etwa der Ausbau der Conditions. Es wäre z.B. sinnvoll, mehrere Kontrollen für eine Abfrage zu ermöglichen.
Projekte wie DynaFlex leben naturgemäß nur durch die Nutzung innerhalb anderer Projekte. Dadurch begründet sich ihre Existenz. Wenn Sie also eine Extension entwickeln oder entwickelt haben und der Meinung sind, diese könnte von DynaFlex-Funktionen profitieren, dann scheuen Sie sich nicht, den Autor anzusprechen. Dies gilt auch, wenn Sie DynaFlex bereits einsetzen und die eine oder andere Funktionalität vermissen. Über den aktuellen Stand der Entwicklung kann man sich auf der Webseite des Autors [5] informieren.
Risiken und Nebenwirkungen
Momentan müssen noch einige Dinge bei der Benutzung von DynaFlex, wie das Verhalten von TYPO3 in Zusammenhang mit FlexForms, beachtet werden. FlexForms sind in zwei Teile gegliedert. Auf der einen Seite findet sich die Struktur des Formulars. Diese Strukturen befinden sich im TCA. Die eigentlichen Daten werden in einer weiteren XML-Struktur in der Datenbank gespeichert. Diese Datenstrukturen enthalten alle definierten Felder mit den eingetragenen Werten. Hierbei kommt das Problem zum Vorschein, dass alte Strukturen nicht aus den XML-Daten entfernt werden. Dies ist unproblematisch, solange sich die Struktur des Formulars nicht ändert. Doch genau das bewirkt DynaFlex.
Wenn beispielsweise zwei Texteingabefelder erzeugt und die Eingaben des Users in die Datenbank eingetragen werden, entsteht etwa folgendes XML im Feld der Datenbank:
<T3FlexForms>
<data type="array">
<sDEF type="array">
<lDEF type="array">
<field1 type="array">
<vDEF>value 1</vDEF>
</field1>
<field2 type="array">
<vDEF>value 2</vDEF>
</field2>
</lDEF>
</sDEF>
</data>
</T3FlexForms>
Listing 3
Wenn aufgrund der DynaFlex-Konfiguration z.B. das Feld „field2“ nicht mehr eingefügt wird, verbleibt dessen Wert weiterhin in der Datenbank. Das ist für TYPO3 prinzipiell kein Problem, kann aber bei der Auswertung der Daten im Frontend zu Problemen führen. Zum Beispiel dann, wenn man alle Daten der FlexForm ausgibt ohne zu prüfen, ob diese noch relevant sind. Eine weitere Folge des Problems besteht darin, dass das Feld wieder die alten Daten anzeigt, sobald es erneut in das Formular aufgenommen wird. Momentan muss man das Löschen der Daten in den FlexForms noch selbst verwalten und implementieren. Es wird jedoch an einer Lösung gearbeitet, die dieses "Aufräumen" der FlexForm-Daten automatisch nach dem Erzeugen der Formulare durchführt.














