Mit dem Rendering kann man sich das lästige Markupschreiben sparen und einfach per YAML konfigurieren.
Hier nochmal die Rendering-Konfiguration aus Beispiel 1 (einfaches Formular):
renderer: # hier werden die Renderer des Formulars definiert
default: # Der Standard-Renderer hat den Namen default
_type: form # das Root-Widget ist vom Typ form
id: 'contact' # DOM-ID des <form>
fields: # fields gibt die Kinder-Widgets an
_info: { _type: form_message, wrap: {div: status } } # Das form_message-Widget gibt die Formularmeldung aus. Über den wrap-Parameter wird es mit einem <div class="status"> umschlossen.
name: { _type: text, label: 'Name:' } # <input> und <label> für name
email: { _type: text, label: 'E-Mail:' } # <input> und <label> für email
message: { _type: textarea, label: 'Nachricht:' } # <textarea> und <label> für message
_submit: { _type: submit, value: 'Senden' } # submit-Button
durch
<?php echo WV30_Form::getForm('contact', true)->render() ?>
wird folgendes Markup wird vom Renderer generiert (und auch das Form verarbeitet).
<form method="post" action="kontakt/" id="contact">
<div style="display:none"><input type="hidden" value="contact" name="__wv30_form_id"></div>
<div class="status"></div>
<label for="default_name">Name:</label>
<input type="text" value="" name="name" id="default_name">
<label for="default_email">E-Mail:</label>
<input type="text" value="" name="email" id="default_email">
<label for="default_message">Nachricht:</label>
<textarea name="message" rows="3" cols="25" id="default_message"></textarea>
<input type="submit" value="Senden" id="default__submit">
</form>
Natürlich bekommen die Elemente ggf. Error-Klassen und geben die Nutzereingaben wieder aus, falls beim Abschicken Fehler auftraten.
Ein Formular kann mehrere Renderer haben. Hat es nur einen, sollte man diesen “default” nennen.
Unter default wird das Root-Widget des Renderers definiert. _type gibt den Typ des Widgets an. Abhängig davon wird die entsprechende Widgetklasse instanziiert.
Im Beispiel WV30_Widget_Form weil Typ gleich form ist. Das WV30_Widget_Form sorgt für das <form>-Tag. Es bekommt auch die zugewiesene id. Die id ist gleichzeitig Prefix für die untergeordneten Widgets. Außerdem rendert es das versteckte Feld mit dem Formular-Key.
WV30_Widget_Form und andere Eltern-Widgets haben den Parameter fields. In einem assoziativen Array können hier untergeordnete Widgets definiert werden. Die Keys des Arrays können sich - müssen aber nicht - auf Formular-Felder beziehen. Beziehen sich die Keys nicht auf Formular-Felder haben die zugeordneten Widgets nur einen strukturellen oder informativen Nutzen. Im Beispiel beginnen diese Keys mit einem Unterstrich (_info, _submit). Die Werte des assoziativen Arrays sind Widgetdefinitionen genau wie beim Root-Widget. Verpflichten ist hier zumindest der _type-Parameter der widget die Widgetklasse festlegt.
Es existieren eine Reihe allgemeiner Parameter, die alle Widgets akzeptieren. Die wichtigsten sind wrap, label, class und label_after. Genauer sind diese weiter unten bei “Parameter aller Widgets” beschrieben.
Das Rendering des Formulars geschieht über Widgets. Dies sind Klassen, die von WV30_Widget abgeleitet sind.
Die Klasse WV30_Widget stellt eine Reihe wichtiger Basisfunktionalitäten bereit, die eigentlich jedes Widget benötigt. Alle Widgets lassen sich über Parameter konfigurieren. Damit man die Übersicht über alle Parameter behält werden alle möglichen Parameter als Konstanten der Klasse definiert (siehe Quellen von WV30_Widget).
Ein sehr wichtiger Paramter der Klasse WV30_Widget ist fields. Über fields kann man eine Liste von Kinder-Widgets angeben, die das Widget rendern soll. Die Kinder-Widgets können möglicher Weise wieder Kinder-Widgets haben usw. Dadurch ergibt sich ein Widget-Baum. Alle Widgets, die Kinder-Widgets rendern sollen, müssen direkt von WV30_Widget oder von Klassen, die die Methode doRender() nicht überschrieben haben, abgeleitet werden. Ein Beispiel dafür ist WV30_Widget_Form (form).
Ein fields-Eintrag hat als Key den Formularfeld-Namen und als Value die Parameter des Widgets. Dabei muss einer der beiden speziellen Paramter _type oder _class angegeben sein, damit das Eltern-Widget die Klasse des Widgets herausfinden kann: _class ist direkt der Klassenname - _type der Name des Widgets wie er in der static.yml oder in der _static-Sektion der Konfiguration definiert ist.
Bezieht sich ein fields-Eintrag nicht direkt auf ein Formularfeld, so kann als Key einfach ein beliebiger anderer freie Namen verwendet werden. Zur Übersichtlichkeit haben diese im Beispiel einen Unterstrich als Prefix (_info, _submit).
Die meisten Widgets überschreiben die Methode doRender() und liefern das gewünschte Markup zurück. Zum Rendern des Widgets wird aber nicht direkt die doRender() aufgerufen, sondern die render()-Methode. Dies sorgt dafür, dass Goodies wie die Parameter wrap, wrap_inner, label, label_after, item_class usw. funktionieren. Zur Entwicklung eigener Widgets sei vorerst auf die Quellen verwiesen. Die vorhandenen Widgets sind leicht verständlich und sollten als Grundlage für Widget ausreichend sein.
Hat man alternative Ansichten für das gleiche Formular, nimmt man nicht den Namen default, sondern weitere Namen und ruft den entsprechenden Renderer so auf:
<?php echo WV30_Form::getForm('contact', true)->render('alternative1') ?>
Widget mit DOM-Element wrappen
Hier ein etwas komplexes aber noch relativ einfaches Beispiel:
kontakt:
name: Kontakt
fields: [name, firma, email, telefon, zeiten, nachricht]
required: [name, email, nachricht]
validators:
email: mail
processors:
mail:
type: mail
renderer:
default:
_type: form
id: 'form_kontakt'
wrap: {div: form}
fields:
_head: { _type: plaintext, text: 'Schreiben Sie uns!', wrap: h4}
_info: { _type: form_message, wrap: {div: status } }
_main:
_type: list
wrap_item: {div: widget }
fields:
name: { _type: text, label: 'Name*:' }
firma: { _type: text, label: 'Firma:' }
email: { _type: text, label: 'E-Mail-Adresse*:' }
telefon: { _type: text, label: 'Telefon:' }
zeiten: { _type: text, label: 'Gewünschte Rückrufzeiten:' }
nachricht: { _type: textarea, label: 'Nachricht*:' }
honeypots: { _type: honeypots, wrap: {div: pot} }
_footer:
_type: list
wrap: {div: footer}
fields:
_note: {_type: plaintext, text: '* Pflichtangaben', wrap: span }
_submit: { _type: submit, value: 'Senden', wrap: {div: submit}, label: ' ', label_after: ' ' }