PHP Magazin

PHP, JavaScript, Open Web Technologies
X

Unser neues Quickvote: Welche PHP-Version nutzt ihr?

In Form gebracht

Eine Einführung in die Zend_Form-Komponente des Zend Frameworks

Ralf Eggert

Im März 2008 ist das Release 1.5 des Zend Frameworks erschienen und brachte der Entwicklergemeinschaft endlich die lang ersehnte Komponente zur Formularverarbeitung. Mit Zend_Form können PHP-Entwickler Formulare definieren, ausgeben sowie die Formulardaten filtern und validieren. Dieser Artikel bietet einen Einblick in die vielfältigen Möglichkeiten der Zend_Form-Komponente.

Schon die erste Version des Zend Frameworks vor mehr als zwei Jahren hat eine erste Zend_Form-Komponente enthalten, wenn auch nur im Inkubatorpfad des Frameworks. Diese frühe Version ist nie über diesen Alphastatus hinausgekommen und wurde auch nicht für den produktiven Einsatz empfohlen. In der Folgezeit entstanden insgesamt drei verschiedene neue Vorschläge (Proposals) für die Implementierung einer Zend_Form-Komponente. An einem der Vorschläge habe ich selbst teilweise mitgewirkt. Jedoch kamen auch diese drei Proposals nie über ihren Vorschlagsstatus hinaus.

Ende 2007 nahm sich dann Matthew Weier O'Phinney der fehlenden und von vielen Seiten geforderten Formkomponente an. In seinem Proposal griff er nicht nur die Ideen der drei anderen Vorschläge auf, auch andere Formularkomponenten hat Matthew in Betracht gezogen. Die Umsetzung seines Vorschlages ist rechtzeitig für das 1.5.0 Release fertiggeworden und zählt für viele Nutzer des Zend Frameworks zu den wichtigsten Komponenten des gesamten Frameworks.

Dieser Artikel basiert auf dem 1.5.1-Release. Die Beispiele sollten in der Regel auch mit späteren Versionen des Zend Frameworks laufen.

Was bietet Zend_Form?

Eine gute Einführung in die Zend_Form-Komponente bietet das Manual. Dort ist nicht nur eine Anleitung für den Schnellstart enthalten. Das Manual bietet auch einen Überblick über die vorhandenen Formularelemente und Dekorierer, geht auf das Erstellen von Formularelementen und Formulare ein und erläutert fortgeschrittene Funktionen wie Internationalisierung und mehrseitige Formulare.

Zusammengefasst stellt Zend_Form Funktionalitäten für das Filtern und Validieren von Formulareingaben, das Anordnen und Gruppieren einzelner Elemente, das Rendern der Formulare und seiner Elemente und die Konfiguration auf Element- und Formularebene bereit. Die Komponente interagiert dabei stark mit anderen Framework-Komponenten wie Zend_Config, Zend_Validate und Zend_Filter. Aber auch die Integration von Zend_View und Zend_Translate steht bereits zur Verfügung.

Die Formulare können Schritt für Schritt durch Verwendung eines Zend_Form-Objekts, durch Erweiterung der Zend_Form-Klasse in einer eigenen Klasse oder mithilfe von Zend_Config erstellt werden. Dies betrifft nicht nur die Definition der einzelnen Elemente, auch das Hinzufügen von Filtern und Validatoren ist bei allen Ansätzen problemlos möglich.

Einige Vorbereitungen

Um die Beispiele in diesem Artikel nachvollziehen zu können, empfiehlt sich der Aufbau einer Verzeichnisstruktur wie in Abbildung 1 dargestellt. Das aktuelle Release des Zend Frameworks kann von der Website heruntergeladen und im Verzeichnis /library kopiert werden.



Abb. 1: Verzeichnisstruktur

Alle Dateien der Beispielanwendung für diesen Artikel finden Sie auch hier zum Download. Insgesamt sind vier Beispiele für die verschiedenen Ansätze zur Formularerstellung enthalten. Erstellt wird ein einfaches Kontaktformular (Abb. 2).



Abb. 2: Kontaktformular

Auf die Einrichtung der Bootstrap-Datei sowie die generelle Arbeitsweise der Zend_Controller-Komponente im Zusammenspiel mit Zend_View kann an dieser Stelle nicht detailliert eingegangen werden. Eine gute Einführung in den grundlegenden Aufbau einer Zend-Framework-Anwendung bietet das Manual in der Schnellstartanleitung zur Zend_Controller-Komponente. Sämtliche Dateien für den Grundaufbau sind aber auch unter unseren Downloads zu finden.

Verwendung eines Zend_Form-Objekts

Der einfachste Ansatz, um ein Formular zu erstellen, ist die Verwendung eines Zend_Form-Objekts. Ein kurzes Beispiel (das noch nicht alle Felder unseres Kontaktformulars enthält) finden Sie in Listing 1. Zuerst wird ein Objekt von der Klasse Zend_Form instantiiert. Über einfache Methoden wie setAction() oder setMethod() können Einstellungen auf der Formularebene verändert werden.

Als nächstes wird ein normales Textfeld mit Namen name und vom Typ text erstellt. Die meisten Einstellungen können sowohl direkt an die createElement()-Methode des Formobjekts übergeben als auch über separate Methoden des Elementobjektes verändert werden. Dabei implementieren sowohl Zend_Form als auch die Zend_Form_Element-Klassen das so genannte Fluent-Interface, bei dem die Methodenaufrufe miteinander verknüpft werden können. Für das Element name werden zudem zwei Validatoren hinzugefügt, um eine bestimmte String-Länge sowie nur Buchstaben als Eingabe zu erlauben.

Anschließend wird ein Absendebutton für das Formular erstellt und beide Elemente zum Formularobjekt mithilfe der addElement()-Methode hinzugefügt. Listing 2 zeigt das komplette Formular aus Abbildung 2 und enthält unter anderem ein mehrzeiliges Textfeld, eine Auswahlliste, eine Checkbox und einen Radiobutton.

Um das Formular darstellen zu können, benötigen Sie als nächstes ein Template für die Ausgabe. Listing 3 zeigt, dass sich die Ausgabe des kompletten Formulars im Template sehr einfach gestaltet. Ein einfaches echo() des Formularobjekts reicht aus, damit alle Elemente des Formulars ausgegeben werden.

Wie das Formularobjekt zusammen mit Zend_Controller und Zend_View zu verwenden ist, zeigt Listing 4. In dieser Action-Methode des Controllers wird zuerst das Formobjekt mit einer Hilfsmethode buildForm1() erstellt. Diese Hilfsmethode enthält im Wesentlichen den Programmcode aus Listing 2. Das Formobjekt sowie ein Seitentitel werden an das View-Objekt übergeben. Danach wird das Template (Listing 3) mithilfe der render()-Methode ausgegeben. Das Ergebnis sieht in etwa so aus wie das Formular in der Abbildung 2. Die Darstellung des Formulars klappt schon einmal ausgezeichnet.

Verarbeitung des Formulars

Listing 5 zeigt die Action-Methode des Controllers, die nach dem Absenden des Formulars aufgerufen wird. Diese wurde über die setAction()-Methode des Formularobjekts festgelegt. Wird die Methode nicht per POST-Request aufgerufen, wird die Verarbeitung an die Methode zur Anzeige des Formulars weitergeleitet.

Damit die Validierung durchgeführt werden kann, wird zuerst auch das Formularobjekt mit der Hilfsmethode buildForm1() erstellt. Danach erfolgt die Validierung der Formulardaten anhand der für das Formular definierten Validatoren. War die Validierung erfolglos, wird das Formular erneut ausgegeben - dieses Mal aber mit den bereits eingegebenen Formulardaten. Bei erfolgreicher Validierung werden die Daten in unserem Beispiel einfach nur ausgegeben.

Abbildung 3 zeigt das Formular nach dem Absenden mit fehlerhaften Daten. Die Fehlermeldungen sind momentan noch in Englisch und stammen direkt aus den verwendeten Zend_Validator-Klassen. Wie Sie diese Meldungen internationalisieren können, erfahren Sie weiter unten.



Abb. 3: Fehlermeldungen

Der grundlegende Ablauf für die Erstellung und Verarbeitung eines Formulars mithilfe von Zend_Form ist somit dargelegt. Als nächstes sehen Sie, wie Sie durch Erweiterung der Zend_Form-Klasse die Wiederverwendbarkeit Ihrer Formulare verbessern können.

Erweiterung der Zend_Form-Klasse

Um ein Formular besser wiederverwenden zu können, empfiehlt es sich, eine eigene Klasse zu erstellen, die von der Zend_Form-Klasse abgeleitet wird. Ein einfaches Beispiel (das nicht alle Felder unseres Beispielformulars enthält) finden Sie in Listing 6. Die Klasse ContactForm2 erweitert die Klasse Zend_Form. Sämtliche Anpassungen sollten in der Methode init() erfolgen, da diese extra hierfür geschaffen wurde und im Konstruktor von Zend_Form aufgerufen wird.

Der Programmcode der Klasse ContactForm2 ähnelt dem in Listing 1 sehr - mit dem Unterschied, dass direkt auf die aktuelle Instanz des Objektes zugegriffen wird. Die komplette Klasse ContactForm2 mit allen Elementen finden Sie in Listing 7.

Die Verarbeitung des Formulars unterscheidet sich von dem ersten Ansatz nur im Detail (Listing 8). Statt die Hilfsmethode buildForm1() des Controllers zu verwenden, kann die Formklasse ContactForm2 direkt instantiiert werden. Die Klasse wird übrigens automatisch geladen, da in der Bootstrap-Datei (Listing 9) der Autoloader des Zend Frameworks verwendet wird und das Verzeichnis mit den Formularklassen dem include_path hinzugefügt wurde.

Durch die Erweiterung der Zend_Form-Klasse wird nicht nur die Wiederverwendbarkeit eines Formulars erhöht. Auch die Action-Methoden im Controller werden entschlackt, da dort nur die Interaktion zwischen Controller, View und Formular erfolgen muss. Die Definition des Formulars ist sauber in der Klasse ContactForm2 gekapselt.

Dekorieren macht Laune

Zend_Form macht sehr viel Gebrauch vom Dekorierer - einem bekannten Entwurfsmuster. Hierdurch wird das verschachtelte Rendern des Formulars und seiner Elemente ermöglicht. Eine Einführung in Zend_Form_Decorator findet sich im Manual. Zudem hat der Autor der Zend_Form-Komponente auch ein sehr gutes Tutorial über die Verwendung der Dekorierer mit Zend_Form geschrieben, in dem auch der genaue Ablauf beschrieben wird.

Standardmäßig werden die Formulare mithilfe von Definitionslisten in HTML per <dt>, <dt> und <dd> gerendert. Wie der HTML-Code aussieht, entnehmen Sie bitte Listing 10. Möchte man dieses Verhalten ändern, um das Formular z.B. mithilfe von Tabellen oder per CSS und tabellenlosem Design darzustellen, müssen die Standarddekorierer überschrieben bzw. angepasst werden.

Wir möchten das Formular nun nicht mit Definitionslisten, sondern mit einem tabellenlosen Design und CSS umsetzen. Wenn Sie sich noch einmal Listing 7 genauer anschauen, enthält die Klasse ContactForm2 nicht nur eine init()-Methode. Zum einen werden einige Klasseneigenschaften definiert und zum anderen wird die Methode loadDefaultDecorators() überschrieben.

Durch das Überschreiben der Methode loadDefaultDecorators() erreichen wir, dass die Dekorierer auf Formularebene geändert werden. Es werden drei Dekorierer definiert, die von Innen nach Außen verarbeitet werden. Der Dekorierer "FormElements" rendert alle Formularelemente. Diese werden dann mithilfe des Dekorierers "HtmlTag" von einem <div>-Tag umschlossen. Ganz außen fasst der Dekorierer "Form" dann alles in einen <form>-Tag ein.

In der init()-Methode werden die Dekorierer durch den Aufruf der setElementDecorators()-Methode für alle Elemente verändert. Statt der <dt>- und <dd>-Tags werden ebenfalls <div>-Tags verwendet, um das Formular mithilfe einiger CSS-Klassen in das tabellenlose Design zu bringen. Den entsprechenden HTML-Code finden Sie in Listing 11.

Eine weitere kleine Anpassung ist ebenfalls noch in der init()-Methode enthalten. So bekommt die Checkbox des newsletter-Elements abweichende Dekorierer zugeteilt, um einen weiteren Beschreibungstext zuordnen zu können. Und der Absendebutton wird ohne ein zusätzliches Label ausgegeben, weil der Labeltext ja bereits auf dem Button selbst angezeigt wird.

Mithilfe der Dekorierer lässt sich das Rendern des Formulars in HTML sehr detailliert anpassen. Ein Beispiel für ein Formular im Tabellenlayout, wie man eigene Dekorierer erstellt und wie man z.B. Checkboxen gruppiert, ist ebenfalls in dem angesprochenen Tutorial erläutert.

Formularobjekt mit Zend_Config erstellen

Für alle, die die Details ihrer Formulare nicht in PHP-Klassen vorhalten und stattdessen lieber mit INI- oder XML-Dateien arbeiten möchten, bietet Zend_Form auch die Verwendung von Zend_Config-Objekten an. Listing 12 zeigt das gleiche Formular von eben, nur in XML definiert. Der Schreibaufwand ist durch die Verschachtelung in XML natürlich deutlich höher, dafür können alle Einstellungen im XML-Format bearbeitet werden.

Listing 13 zeigt die neue Formklasse ContactForm3, welche die XML-Datei verwendet. Nach dem Einlesen der Konfigurationsdaten wird das Zend_Config_Xml-Objekt mithilfe der Methode setConfig() übergeben. Zend_Form kümmert sich dann um den Rest. Da, wie oben erwähnt, die Methode setElementDecorators() immer auf alle Elemente angewendet wird, müssen zum Ende der init()-Methode von ContactForm3 die individuellen Dekorierer für die Checkbox und den Submit-Button nochmals an die beiden Elemente übergeben werden. Die Ausgabe des Formulars in HTML ist identisch mit der aus dem vorherigen Beispiel (Listing 14).

Um die Performance bei der Verwendung der XML-Dateien zu erhöhen, würde es sich z.B. anbieten, das Zend_Config_Xml-Objekt nach dem ersten Lesen mithilfe von Zend_Cache als serialisiertes Array in einer Datei oder im MemCache zu speichern. Dann sollte nur jeweils der Cache nach dem Ändern der XML-Dateien gelöscht werden, bevor das Formular erneut aufgebaut wird.

Ob Sie lieber eine XML-Datei oder eine erweiterte Klasse verwenden, bleibt Ihnen überlassen. Etwas flexibler ist der Einsatz einer erweiterten Klassen, da der Ablauf der setConfig()-Methode festgelegt ist, d.h. es ist genau definiert, in welcher Reihenfolge welche Methoden aufgerufen werden. Verwenden Sie nicht setConfig(), sondern kümmern sich selbst um den Formularaufbau, haben Sie einen besseren Einfluss.

Formulare mit Zend_Translate internationalisieren

Weiter oben haben Sie gesehen, dass die Fehlermeldungen bisher nur in Englisch ausgegeben werden. Dies liegt daran, dass die Zend_Validate-Klassen ihre Fehlermeldungen nur in englischer Sprache vorhalten. Neben den nicht übersetzen Fehlermeldungen haben wir bisher alle im Formular verwendeten Bezeichnungen nur in deutscher Sprache direkt verwendet. Ein Wechsel in eine andere Sprache ist mit diesen statischen Texten nicht ohne Weiteres möglich. In unserem letzten Beispiel möchten wir nun Übersetzungen für Deutsch und Englisch anbieten.

Zend_Form bringt bereits eine integrierte Unterstützung für die Verwendung von Zend_Translate mit. Dazu empfiehlt sich auch ein Blick in das Manual, in dem der Einsatz von Zend_Translate zusammen mit Zend_Form erläutert wird.

Zuerst erstellen wir eine neue Formklasse ContactForm4, die ähnlich aufgebaut ist, wie die Klasse ContactForm2 (Listing 7). Der Unterschied ist der, dass sämtliche Texte, die an die Methoden setLabel(), setDescription(), setMultiOptions() und setMultiOption() durch ein Textkürzel ersetzt werden. Das Ergebnis finden Sie in Listing 15.

Als Nächstes erstellen wir nun die Dateien mit den Übersetzungen für die verwendeten Texte. Zend_Translate kann mit einer einfachen PHP-Datei arbeiten, die nur ein Array zurückgibt. Zend_Translate bietet aber noch Unterstützung für viele weitere Formate wie Gettext, CSV oder TBX. In den beiden Übersetzungsdateien müssen wir für jeden der in ContactForm4 verwendeten Textkürzel eine Übersetzung angeben. Dies gestaltet sich recht einfach. Doch was machen wir nun mit den Fehlermeldungen der Zend_Validate-Klassen?

Die Übersetzung dieser Fehlermeldungen wird ebenfalls durch Zend_Form bereitgestellt. Wir müssen nur die Textkürzel ermitteln, die von den Zend_Validate-Klassen verwendet werden. Dazu untersuchen wir die von unserem Formular verwendeten Zend_Validate-Klassen. Dies sind Zend_Validate_NotEmpty (wird durch die setRequired()-Methode aufgerufen), Zend_Validate_Alpha, Zend_Validate_StringLength, Zend_Validate_EmailAddress sowie Zend_Validate_Hostname (wird durch Zend_Validate_EmailAddress verwendet).

In diesen Zend_Validate-Klassen finden Sie jeweils eine Liste von Konstanten, die die gesuchten Textkürzel definieren. Für Zend_Validate_StringLength wären dies z.B. stringLengthTooShort und stringLengthTooLong. Alle diese Kürzel übernehmen wir in unsere Übersetzungsdateien. Dabei können wir als Übersetzungsvorlage auch gleich die in den Zend_Validate-Klassen vorhandenen Fehlermeldungstexte mit übernehmen.

Das Ergebnis finden Sie in den Listings 16 (deutsch) und 17 (englisch). Wie Sie sehen, können sie in den Fehlermeldungen auch jeweils die Platzhalter verwenden, die die Zend_Validate-Klassen bereitstellen. Das erleichtert den Satzbau und die Anordnung von Variablen für die verschiedenen Sprachen. Die englische Übersetzungsdatei enthält übrigens nicht die Fehlermeldungen, da Zend_Form die Texte direkt aus den Zend_Validate-Klassen verwenden kann. Lediglich, wenn Sie mit der originalen Formulierung nicht zufrieden sind, bietet es sich an, den Text entsprechend anzupassen, damit auch Benutzer ohne großen technischen Hintergrund mit den Fehlermeldungen etwas anfangen können.

Als Letztes müssen wir ein Zend_Translate-Objekt erstellen, dem Objekt die Übersetzungsdateien mitteilen, eine Standardsprache festlegen und zum Schluss Zend_Form mitteilen, dass ein Zend_Translate Objekt für die Übersetzung der Texte bereitsteht. Listing 18 zeigt den erforderlichen Programmcode, den Sie in die Bootstrap-Datei (zu finden unter /public/index.php) einfügen müssen.

Wenn Sie jetzt das vierte Beispielformular aufrufen, sehen Sie, dass alle Texte inklusive der Fehlermeldungen übersetzt werden. Ändern Sie in der Bootstrap-Datei das Locale auf en_US, erscheint das Formular komplett in Englisch.

Fazit und Ausblick

In diesem Artikel haben Sie mehrere Ansätze kennengelernt, um mithilfe von Zend_Form Formulare zu definieren und zu verarbeiten. Sie haben den Einsatz der Zend_Validate- und Zend_Filter-Klassen sowie die Verwendung von Zend_Config zur Definition von Formularen an praktischen Beispielen gesehen. Auch die Internationalisierung Ihrer Formulare mithilfe von Zend_Translate sollte nach der Lektüre hoffentlich keine große Hürde mehr sein.

In Gesprächen mit anderen Entwicklern kam in der Vergangenheit mehrmals ein Kritikpunkt an Zend_Form auf, der sich auf das Rendern der Formulare in einem Zend_View-Skript bezieht. Zend_Form stellt mit der render()-Methode derzeit nur eine einzige Möglichkeit für das Rendern des vollständigen Formulars bereit. Manche Entwickler wünschen sich an dieser Stelle mehr Flexibilität.

Matthew hat diesen Wunsch bereits erhört und einen eigenen Vorschlag für eine Erweiterung gemacht. Da diese Erweiterung nicht so kompliziert zu sein scheint, wird sie wahrscheinlich bereits in einem der nächsten Releases verfügbar sein.

Ralf Eggert ist Geschäftsführer der Travello GmbH mit Sitz in Pinneberg. Der vor einigen Monaten durchgeführte Relaunch der Reise-Community www.travello.com erfolgte komplett auf Basis des Zend Frameworks. Kontakt: r.eggert@travello.com

 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).