Development

Documentation/de_DE/book/1.0/10-Forms

You must first sign up to be able to contribute.

Version 16 (modified by Jan.Kunzmann, 10 years ago)
version bump

ARBEITSENTWURF ROHÜBERSETZUNG / TRANSLATION WORKING DRAFT
Originaldokument: http://svn.symfony-project.com/doc/branches/1.1/book/10-Forms.txt, Revision 8468 vom 2008-04-04
Übersetzungsfortschritt: 100%
Übersetzung: JK
Korrekturfortschritt: 0%

Kapitel 10 - Formulare

Wer Templates schreibt, steckt auch eine Menge Zeit in die Programmierung von Formularen - und trotzdem ist deren Design im Allgemeinen eher schlecht. Weil viel Aufmerksamkeit erforderlich ist, mit Defaultwerten, Formatierungen, Überprüfung (Validierung), Wiederbefüllung und der Formularverarbeitung an sich umzugehen, neigen Entwickler dazu, einige wichtige Details bei diesem Prozess zu überfliegen. Dementsprechend widmet Symfony diesem Thema besondere Aufmerksamkeit. Dieses Kapitel beschreibt die Werkzeuge, die viele dieser Aufgaben automatisieren und die Entwicklung von Formularen beschleunigen.

  • Mit den Formular-Helfern steht Ihnen ein schnellerer Weg zur Verfügung, um in Templates Eingabefelder zu erzeugen, insbesonders für so komplexe Elemente wie Datumseingaben, Dropdown-Listen und formatierten Text (Rich-Text).
  • Wenn ein Formular dazu dienen soll, die Eigenschaften eines Objekts zu bearbeiten, können die Objekt-Form-Helfer die Entwicklung noch weiter beschleunigen.
  • Die YAML-Validierungsdateien erleichtern die Überprüfung und Wiederbefüllung
  • Validatoren verschlanken den Code, der zum Überprüfen der Eingaben notwendig ist. Symfony besitzt Validatoren für die gebräuchlichsten Anwendungsfälle, und es ist sehr einfach, eigene Validatoren zu schreiben.

Formular-Helfer

In einem Template werden häufig die HTML-Tags der Formularelemente mit PHP-Code gemischt. Die Formular-Helfer in symfony sind dazu gedacht, diese Aufgabe zu vereinfachen und zu vermeiden, dass mitten in einem <input>-Tag mehrfach ein <?php echo stehen muss.

Main Form Tag

Wie bereits im vorangegangenen Kapitel erklärt wurde, sollten Sie den form_tag()-Helfer verwenden, um ein Formular zu erzeugen, denn er übersetzt die als Parameter übergebene Ziel-Adresse des Formulars in eine geroutete URL. Als zweiten Parameter können Sie weitere Optionen übergeben -- z.B. um die Übermittlungsmethode (method), das Encoding (enctype) oder andere Attribute anzugeben. Codeabschnitt 10-1 zeigt einige Beispiele.

Listing 10-1 - Der form_tag()-Helfer

[php]
<?php echo form_tag('test/save') ?>
 => <form method="post" action="/pfad/zum/speichern">

<?php echo form_tag('test/save', 'method=get multipart=true class=simpleForm') ?>
 => <form method="get" enctype="multipart/form-data" class="simpleForm"action="/pfad/zum/speichern">

Weil es keinen Bedarf für einen Helfer gibt, der ein Formular schließt, sollten Sie einfach das HTML-Tag </form> verwenden, auch wenn das in Ihrem Quelltext nicht wirklich gut aussehen mag.

Standard Form Elements

Die Formular-Helfer vergeben jedem Formularelement ein ID-Attribut, das standardmäßig von dem Attribut name abgeleitet wird. Dies ist aber nicht die einzige nützliche Konvention. Codeabschnitt 10-2 zeigt Ihnen eine Übersicht über die Standard-Formular-Helfer und ihre Optionen.

Codeabschnitt 10-2 - Syntax der Standard-Formular-Helfer

[php]
// Textfeld (input)
<?php echo input_tag('name', 'Standardwert') ?>
 => <input type="text" name="name" id="name" value="Standardwert" />

// Allen Formular-Helfer können weitere Optionen übergeben werden. Dies ermöglicht
// es Ihnen, dem erzeugten Tag spezifische Attribute hinzuzufügen
<?php echo input_tag('name', 'Standardwert', 'maxlength=20') ?>
 => <input type="text" name="name" id="name" value="Standardwert" maxlength="20" />

// Feld für mehrzeiligen Text (textarea)
<?php echo textarea_tag('name', 'Standardinhalt', 'size=10x20') ?>
 => <textarea name="name" id="name" cols="10" rows="20">
      Standardinhalt
    </textarea>

// Checkbox
<?php echo checkbox_tag('single', 1, true) ?>
<?php echo checkbox_tag('driverslicense', 'B', false) ?>
 => <input type="checkbox" name="single" id="single" value="1"checked="checked" />
    <input type="checkbox" name="driverslicense" id="driverslicense" value="B" />

// Radiobutton
<?php echo radiobutton_tag('status[]', 'value1', true) ?>
<?php echo radiobutton_tag('status[]', 'value2', false) ?>
 => <input type="radio" name="status[]" id="status_value1" value="value1" checked="checked" />
    <input type="radio" name="status[]" id="status_value2" value="value2" />

// Dropdown-Liste (select)
<?php echo select_tag('payment',
  '<option selected="selected">Visa</option>
   <option>Eurocard</option>
   <option>Mastercard</option>')
?>
 => <select name="payment" id="payment">
      <option selected="selected">Visa</option>
      <option>Eurocard</option>
      <option>Mastercard</option>
    </select>

// Optionsliste für ein Select-Tag
<?php echo options_for_select(array('Visa', 'Eurocard', 'Mastercard'), 0) ?>
 => <option value="0" selected="selected">Visa</option>
    <option value="1">Eurocard</option>
    <option value="2">Mastercard</option>

// Dropdown-Helfer in Kombination mit der Optionsliste
<?php echo select_tag('payment', options_for_select(array(
  'Visa',
  'Eurocard',
  'Mastercard'
), 0)) ?>
 => <select name="payment" id="payment">
      <option value="0" selected="selected">Visa</option>
      <option value="1">Eurocard</option>
      <option value="2">Mastercard</option>
    </select>

// Um benamste Optionsschlüssel zu erhalten, können Sie ein assoziatives Array verwenden.
<?php echo select_tag('name', options_for_select(array(
  'Steve'  => 'Steve',
  'Bob'    => 'Bob',
  'Albert' => 'Albert',
  'Ian'    => 'Ian',
  'Buck'   => 'Buck'
), 'Ian')) ?>
 => <select name="name" id="name">
      <option value="Steve">Steve</option>
      <option value="Bob">Bob</option>
      <option value="Albert">Albert</option>
      <option value="Ian" selected="selected">Ian</option>
      <option value="Buck">Buck</option>
    </select>

// Liste mit Mehrfachauswahl (die ausgewählten Werte können ein Array sein)
<?php echo select_tag('payment', options_for_select(
  array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),
  array('Visa', 'Mastercard'),
), array('multiple' => true))) ?>
 => <select name="payment[]" id="payment" multiple="multiple">
      <option value="Visa" selected="selected">Visa</option>
      <option value="Eurocard">Eurocard</option>
      <option value="Mastercard">Mastercard</option>
    </select>

// Drop-down list with multiple selection (selected values can be an array)
<?php echo select_tag('payment', options_for_select(
  array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),
  array('Visa', 'Mastercard')
), 'multiple=multiple') ?>
 => <select name="payment" id="payment" multiple="multiple">
      <option value="Visa" selected="selected"> 
      <option value="Eurocard">Eurocard</option>
      <option value="Mastercard" selected="selected">Mastercard</option>
    </select>

// Feld zum Dateiupload
<?php echo input_file_tag('name') ?>
 => <input type="file" name="name" id="name" value="" />

// Password-Feld
<?php echo input_password_tag('name', 'Wert') ?>
 => <input type="password" name="name" id="name" value="Wert" />

// Verstecktes Formularfeld
<?php echo input_hidden_tag('name', 'Wert') ?>
 => <input type="hidden" name="name" id="name" value="Wert" />

// Button zum Absenden (Text)
<?php echo submit_tag('Speichern') ?>
 => <input type="submit" name="submit" value="Speichern" />

// Button zum Absenden (Bild)
<?php echo submit_image_tag('submit_img') ?>
 => <input type="image" name="submit" src="/images/submit_img.png" />

Der submit_image_tag()-Helfer benutzt den gleichen Syntax und hat demnach auch die gleichen Vorteile wie image_tag().

NOTE Bei den Radiobutton wird das id-Attribute standardmäßig nicht auf den Wert des Attributs name gesetzt, sondern auf eine Kombination aus den Attributen name und value. Dies ist notwendig, weil Sie in der Regel mehrere Radiobuttons mit identischem Namen auf Ihrem Formular haben werden, um dieses praktische "wähle den vorherigen ab wenn ein neuer gewählt wird"-Feature nutzen zu können. Wenn dabei id=name ist, hätten Sie auf Ihrer Seite mehrere HTML-Tags mit der gleichen id, was strikt verboten ist.

-

SIDEBAR Formulardaten verarbeiten

Wie können Sie die Daten abfragen, die Ihre User durch die Formulare übermittelt haben? Sie stehen in den Request-Parametern, also muss Ihre Action nur $this->getRequestParameter($elementName) aufrufen, um den Wert zu ermitteln.

Praktischerweise können Sie die gleiche Action verwenden, um das Formular anzuzeigen und zu verarbeiten. Je nach verwendeter Request-Methode (GET oder POST) wird entweder das Form-Template aufgerufen oder die Formulardaten verarbeitet und der Request zu einer anderen Action weitergeleitet.

[php]
// In meinmodul/actions/actions.class.php
public function executeEditAuthor()
{
  if (!$this->getRequest()->isMethod('post'))
  {
    // Display the form
    return sfView::SUCCESS;
  }
  else
  {
    // Formulardaten verarbeiten
    $name = $this->getRequestParameter('name');
    ...
    $this->redirect('meinmodul/andereaction');
  }
}

Damit das auch funktioniert, muss das Ziel des Formulars die gleiche Action sein wie diejenige, die es anzeigt.

[php]
// In mymodule/templates/editAuthorSuccess.php
<?php echo form_tag('mymodule/editAuthor') ?>

...

Symfony bietet übrigens auch Formular-Helfer an, um asynchrone Anfragen im Hintergrund abzuschicken. Mehr darüber erfahren Sie im nächsten Kapitel, das sich mit Ajax beschäftigt.

Datumseingabe-Widget

Formulare werden häufig verwendet, um Datumswerte zu ermitteln. Falsch formatierte Datumseingaben sind der Hauptgrund für Fehler beim Auswerten. Der Helfer input_date_tag() unterstützt Ihre User bei der Eingabe von Datumswerten mit einem interaktiven JavaScript-Kalender, sofern sie die Option rich auf true setzen, wie in Abbildung 10-1 gezeigt.

Figure 10-1 - Erweitertes Input-Tag zur Datumseingabe

Erweitertes Input-Tag zur Datumseingabe

Wenn Sie die Option rich weglassen, gibt der Helfer drei <select> Tags aus, die mit entsprechenden Werten für Monat, Tag und Jahr gefüllt sind. Diese Dropdown-Listen können sie auch getrennt anzeigen, indem Sie die entsprechenden Helfer aufrufen (select_day_tag(), select_month_tag(), and select_year_tag()). Als Standardwert zeigen diese Elemente den aktuellen Tag an.. Codeabschnitt 10-3 zeigt Ihnen den Datumseingabe-Helfer.

Listing 10-3 - Datumseingabe-Helfer

[php]
<?php echo input_date_tag('geburtstag', '2005-05-03', 'rich=true') ?>
 => Ein Texteingabe-Feld, kombiniert mit einem Kalender-Widget

// Die folgenden Helfer benötigen die Helfer-Gruppe `DateForm`
<?php use_helper('DateForm') ?>

<?php echo select_day_tag('tag', 1, 'include_custom=Wählen Sie einen Tag') ?>
=> <select name="tag" id="tag">
      <option value="">Wählen Sie einen Tag</option>
      <option value="1" selected="selected">01</option>
      <option value="2">02</option>
      ...
      <option value="31">31</option>
    </select>

<?php echo select_month_tag('monat', 1, 'include_custom=Wählen Sie einen Monat use_short_month=true') ?>
=> <select name="monat" id="monat">
      <option value="">Wählen Sie einen Monat</option>
      <option value="1" selected="selected">Jan</option>
      <option value="2">Feb</option>
      ...
      <option value="12">Dez</option>
    </select>

<?php echo select_year_tag('jahr', 2007, 'include_custom=Wählen Sie ein Jahr year_end=2010') ?>
 => <select name="jahr" id="jahr">
      <option value="">Wählen Sie ein Jahr</option>
      <option value="2006">2006</option>
      <option value="2007" selected="selected">2007</option>
      ...
    </select>

Der input_date_tag()-Helfer akzeptiert alle Werte, welche die PHP-Funktion strtotime() erkennt. In Codeabschnitt 10-4 sehen Sie, welche Formate Sie verwenden können, und Codeabschnitt 10-5 zeigt, welche sie unbedingt vermeiden sollten.

Listing 10-4 - Korrekte Datumsformate in den Datums-Helfern

[php]
// Funktioniert perfekt
<?php echo input_date_tag('test', '2006-04-01', 'rich=true') ?>
<?php echo input_date_tag('test', 1143884373, 'rich=true') ?>
<?php echo input_date_tag('test', 'now', 'rich=true') ?>
<?php echo input_date_tag('test', '23 October 2005', 'rich=true') ?>
<?php echo input_date_tag('test', 'next tuesday', 'rich=true') ?>
<?php echo input_date_tag('test', '1 week 2 days 4 hours 2 seconds', 'rich=true') ?>

// Gibt `null` zurück
<?php echo input_date_tag('test', null, 'rich=true') ?>
<?php echo input_date_tag('test', '', 'rich=true') ?>

Listing 10-5 - Falsche Datumsformate in den Datums-Helfern

[php]
// 0-Datum = 01/01/1970
<?php echo input_date_tag('test', 0, 'rich=true') ?>

// Nicht-Englische Datumsangaben funktionieren nicht
<?php echo input_date_tag('test', '01/04/2006', 'rich=true') ?>

Eingabe von formatiertem Text (Rich-Text)

Eie Eingabe von formatiertem Text ist in einem <textarea>-Tag möglich, da die Widgets TinyMCE und FCKEditor in Symfony integriert wurden. Ähnlich einer Textverarbeitung formatiert man über Buttons in Symbolleisten den Text fett, kursiv oder in anderen Stilen, wie in Abbildung 10-2 gezeigt.

Abbildung 10-2 - Eingabe von formatiertem Text

Eingabe von formatiertem Text

Beide Widgets müssen Sie zunächst installieren. Weil die Vorgehensweise für beide die gleiche ist, wollen wir sie hier nur anhand des TinyMCE beschreiben. Sie müssen den Editor von der Webseite des TinyMCE-Projekts (http://tinymce.moxiecode.com/) herunterladen und in einen temporären Ordner entpacken. Kopieren Sie dann das Verzeichnis tinymce/jscripts/tiny_mce/ in Ihr Projekt in das Verzeichnis web/js/ und definieren sie den Pfad zu der Bibliothek in der Datei settings.yml, wie in Codeabschnitt 10-6 gezeigt.

Listing 10-6 - Einrichten des Pfades zur TinyMCE-Bibliothek

all:
  .settings:
    rich_text_js_dir:  js/tiny_mce

Sobald dies einmal erleidgt ist, könenn Sie den Richtext-Editor in Textareas anschalten, indem Sie die Option rich=true verwenden. Sie können auch weitere Einstellungen für den JavaScript-Editor über die Option tinymce_options angeben. Codeabschnitt 10-7 zeigt Ihnen einige Beispiele.

Listing 10-7 - Textarea mit formatiertem Text

[php]
<?php echo textarea_tag('name', 'Standard-Inhalt', 'rich=true size=10x20')) ?>
=> Ein Eingabebereich für formatierten Text via TinyMCE
<?php echo textarea_tag('name', 'Standard-Inhalt', 'rich=true size=10x20 tinymce_options=language:"de",theme_advanced_buttons2:"separator"')) ?>
=> Ein Eingabebereich für formatierten Text via TinyMCE, mit weiteren Parametern

Auswahl von Land und Sprache

Möglicherweise müssen Sie irgendwann einmal ein Feld zur Landauswahl anzeigen. Da jedoch die Ländernamen nicht in allen Sprachen gleich sind, sollten sich die Länder in einer Dropdownliste entsprechend der vom Culture-Einstellung des Users verhalten (Im Kapitel 13 erhalten Sie weitere Informationen zum Thema Culture). Wie in Codeabschnitt 10-8 gezeigt, erledigt der select_country_tag()-Helfer die ganze Arbeit für Sie: er zeigt lokalisierte Ländernamen an und benutzt die Standard-ISo-Codes als Optionswerte.

Listing 10-8 - Tag-Helfer zur Landauswahl

[php]
<?php echo select_country_tag('land', 'AL') ?>
 => <select name="land" id="land">
      <option value="AF">Afghanistan</option>
      <option value="AL" selected="selected">Albanien</option>
      <option value="DZ">Algerien</option>
      <option value="AS">Amerikanisch-Samoa</option>
  ...

Ähnlich wie der select_country_tag()-Helfer zeigt der select_language_tag()-Helfer eine Liste mit Sprachen, wie in Codeabschnitt 10-9 gezeigt.

Listing 10-9 - Tag-Helfer zur Sprachauswahl

[php]
<?php echo select_language_tag('sprache', 'en') ?>
 => <select name="sprache" id="sprache">
      ...
      <option value="da">Dänisch</option>
      <option value="en" selected="selected">Englisch</option>
      <option value="eo">Esperanto</option>
      <option value="et">Estnisch</option>
      <option value="ee">Ewe-Sprache</option>
      <option value="fj">Fidschianisch</option>
      ...

Formular-Helfer für Objekte

Wenn Sie Formularfelder verwenden, um die Eigenschaften eines Objekts zu bearbeiten, sind die Standard-Helfer oft umständlich. Um zum Beispiel das Attribut telefon_nummer des Objekts Kunde zu ändern, würden Sie in etwa folgendes schreiben:

[php]
<?php echo input_tag('telefon_nummer', $kunde->getTelefonNummer()) ?>
=> <input type="text" name="telefon_nummer" id="telefon_nummer" value="0123456789" />

Symfony bietet Ihnen als Alternative zu Formular-Helfer.die Objekt-Formular-Helfer an, damit sie den Attributnamen nicht wiederholen müssen. Der Objekt-Formular-Helfer ermittelt den Namen und den Default-Wert eines Formularelements aus dem Objekt und dem Methodennamen. Das vorherige Beispiel sieht dann so aus:

[php]
<?php echo object_input_tag($kunde, 'getTelefonNummer') ?>
=> <input type="text" name="telefon_nummer" id="telefon_nummer" value="0123456789" />

Die Ersparnis bei object_input_tag() mag vielleicht nicht ausschlaggeben sein. Aber jeder Formular-Helfer hat einen entsprechenden Objekt-Formular-Helfer, und alle haben sie alle den gleichen Syntax. Dies macht die Erzeugung von Formularen ziemlich einfach. Und deshalb werden die Objekt-Formular-Helfer intensiv für Scaffolding und automatisch erzeugte Administrationsoberflächen (siehe Kapitel 14) verwendet. Codeabschnitt 10-10 listet die Objekt-Formular-Helfer auf.

Listing 10-10 - Syntax der Objekt-Formular-Helfer

[php]
<?php echo object_input_tag($object, $method, $options) ?>
<?php echo object_input_date_tag($object, $method, $options) ?>
<?php echo object_input_hidden_tag($object, $method, $options) ?>
<?php echo object_textarea_tag($object, $method, $options) ?>
<?php echo object_checkbox_tag($object, $method, $options) ?>
<?php echo object_select_tag($object, $method, $options) ?>
<?php echo object_select_country_tag($object, $method, $options) ?>
<?php echo object_select_language_tag($object, $method, $options) ?>

Es gibt allerdings keinen object_password_tag()-Helfer, weil es als schlechter Angewohnheit gilt, in einem Passwortfeld einen Standardwert anzuzeigen, der auf einer vorher getätigten Usereingabe basiert.

ACHTUNG Im Gegensatz zu den normalen Formular-Helfern sind die Objekt-Formular-Helfer nur verfügbar, wenn Sie in Ihrem Template mit use_helper('Object') explizit angeben, dass sie die Helfergruppe Object verwenden wollen.

Die interessantesten Objekt-Formular-Helfer sind objects_for_select() und object_select_tag(), die Dropdown-Listen betreffen.

Dropdown-Listen mit Objekten befüllen

Der options_for_select()-Helfer, den wir weiter oben zusammen mit den anderen Standard-Helfern beschrieben haben, übersetzt ein assoziatives PHP-Array in eine Liste von Optionen, wie im Codeabschnitt 10-11 gezeigt.

Listing 10-11 - Mit options_for_select() eine Optionsliste erzeugen, die auf einem Array basiert

[php]
<?php echo options_for_select(array(
  '1' => 'Stefan',
  '2' => 'Robert',
  '3' => 'Albert',
  '4' => 'Jan',
  '5' => 'Heinrich'
), 4) ?>
 => <option value="1">Stefan</option>
    <option value="2">Robert</option>
    <option value="3">Albert</option>
    <option value="4" selected="selected">Jan</option>
    <option value="5">Heinrich</option>

Angenommen, Sie haben bereits ein Array mit Objekten der Klasse Autor, das aus einer Propel-Abfrage stammt. Wenn Sie nun eine Optionsliste aus diesem Array bauen wollen, müssen Sie über das Array iterieren und dabei die Werte id und name jedes Objekts abfragen, wie in Codeabschnitt 10-12 gezeigt.

Listing 10-12 - Mit options_for_select() eine Optionsliste erzeugen, die auf einem Array von Objekten basiert

[php]
// In der Action
$options = array();
foreach ($autoren as $autor)
{
  $options[$autor->getId()] = $autor->getName();
}
$this->options = $options;

// Im Template
<?php echo options_for_select($options, 4) ?>

Diese Form der Verarbeitung geschieht so häufig, dass Symfony einen Helfer hat, um sie zu automatisieren: objects_for_select() erzeugt eine Optionsliste, die direkt auf einem Array von Objekten basiert. Der Helfer braucht zwei zusätzliche Parameter: den Methodennamen zum Ermitteln des value der Optionsliste, und den Inhalt der entsprechenden option-Tags. Codeabschnitt 10-12 lässt sich also auch in dieser einfachen Form schreiben:

[php]
<?php echo objects_for_select($autoren, 'getId', 'getName', 4) ?>

Das ist elegant und schnell, aber symfony geht noch weiter, wenn Sie mit Fremdschlüssel-Spalten arbeiten.

Erzeugen einer Dropdown-Liste aus einer Fremdschlüssel-Spalte

Die Werte einer Fremdschlüssel-Spalte nehmen die Primärschlüssel-Werte der Einträge in der Fremdtabelle an. Wenn zum Beispiel die Tabelle artikel eine Spalte namens autor_id hat, die ein Fremdschlüssel auf die Tabelle autor ist, so sind die möglichen Werte für diese Spalte all diejenigen, die in der Tabelle autor in der Spalte id stehen. Im Prinzip würde eine Dropdown-Liste, durch die man den Autor eines Artikels festlegen kann, wie in Codeabschnitt 10-13 aussehen.

Listing 10-13 - Mit objects_for_select() eine Optionsliste aus einem Fremdschlüssel erzeugen

[php]
<?php echo select_tag('author_id', objects_for_select(
  AutorPeer::doSelect(new Criteria()),
  'getId',
  '__toString',
  $artikel->getAutorId()
)) ?>
=> <select name="autor_id" id="autor_id">
      <option value="1">Steve</option>
      <option value="2">Bob</option>
      <option value="3">Albert</option>
      <option value="4" selected="selected">Ian</option>
      <option value="5">Buck</option>
    </select>

Der Helfer object_select_tag() erledigt diese Aufgabe für Sie. Er erzeugt eine Dropdown-Liste, die mit den Namen der verfügbaren Datensätze aus der Fremdtabelle gefüllt ist. Der Helfer kann die Fremdtabelle und die Fremdspalte vom Schema ableiten, wodurch der Syntax recht knapp gehalten ist. Codeabschnitt 10-13 sieht jetzt so aus:

[php]
<?php echo object_select_tag($artikel, 'getAutorId') ?>

Der object_select_tag()-Helfer leitet den Namen der entsprechenden Peer-Klasse (im obigen Beispiel AutorPeer) aus dem Methodennamen ab, den Sie als Parameter übergeben. Sie können aber auch eine eigene Klasse angeben, indem Sie im dritten Parameter den Wert related_class setzen. Um die entstehenden <option>-Tags zu füllen, wird die __toString()-Methode für jedes Objekt aufgerufen (Wenn die Methode $autor->__toString() nicht definiert ist, wird statt dessen der Primärschlüssel verwendet). Außerdem wird die Auswahlliste durch den Aufruf von doSelect() mit einem leeren Criteria gefüllt; dieser Aufruf gibt alle Datensätze nach Erzeugungsdatum geordnet zurück. Wenn Sie nur eine Teilmenge der Daten anzeigen möchten, die in einer bestimmten Art und Weise sortiert ist, schreiben Sie einfach eine Methode in der Peer-Klasse, die diese Teilmenge als Array zurückliefert, und geben Sie diese im dritten Parameter als Option peer_method an. Zu guter letzt können Sie auch noch einen leeren oder einen selbstdefinierten Eintrag als erste Auswahloption einfügen, indem Sie die Optionen include_blank bzw. include_custom benutzen. Codeabschnitt 10-14 zeigt die verschiedenen Optionen des object_select_tag() Helfers.

Listing 10-14 - Optionen des object_select_tag()-Helfers

[php]
// Basissyntax
<?php echo object_select_tag($artikel, 'getAutorId') ?>
// Erzeugt die Liste durch den Aufruf AutorPeer::doSelect(new Criteria())

// Eine andere Peerklasse zum Ermitteln der Werte verwenden
<?php echo object_select_tag($artikel, 'getAutorId', 'related_class=Foobar') ?>
// Erzeugt die Liste durch den Aufruf FoobarPeer::doSelect(new Criteria())

// Eine andere Peermethode zum Ermitteln der Werte verwenden
<?php echo object_select_tag($artikel, 'getAutorId','peer_method=getBeruehmtesteAutoren') ?>
// Erzeugt die Liste durch den Aufruf AutorPeer::getBeruehmtesteAutoren(new Criteria())

// Add an <option value="">&nbsp;</option> at the top of the list
<?php echo object_select_tag($artikel, 'getAutorId', 'include_blank=true') ?>

// Füge <option value="">Wählen Sie einen Autor</option> als erste Option ein
<?php echo object_select_tag($artikel, 'getAutorId',
  'include_custom=Wählen Sie einen Autor') ?>

Objekte updaten

Ein Formular, das gänzlich zur Bearbeitung von Objekteigenschaften mittels Objekt-Helfern dient, ist in einer Aktion einfacher zu verarbeiten. Wenn Sie z.B. ein Objekt der Klasse Autor mit den Attributen name, alter und adresse haben, kann das Formular wie in Listing 10-15 gezeigt codiert werden:

Listing 10-15 - Ein Formular, das nur aus Objekt-Helfern besteht

[php]
<?php echo form_tag('autor/update') ?>
  <?php echo object_input_hidden_tag($autor, 'getId') ?>
  Name: <?php echo object_input_tag($autor, 'getName') ?><br />
  Alter:  <?php echo object_input_tag($autor, 'getAlter') ?><br />
  Adresse: <br />
         <?php echo object_textarea_tag($autor, 'getAdresse') ?>
</form>

Die Action update des Moduls autor, die beim Absenden des Formulars aufgerufen wird, kann das Objekt einfach mit der von Propel erzeugten Methode fromArray() aktualisieren, wie in Listing 10-16 gezeigt.

Listing 10-16 - Verarbeitung einer Formularübertragung, die auf Objekt-Helfern bestehen Helfers

[php]
public function executeUpdate ()
{
  $autor = AutorPeer::retrieveByPk($this->getRequestParameter('id'));
  $this->forward404Unless($autor);

  $author->fromArray($this->getRequest()->getParameterHolder()->getAll(), BasePeer::TYPE_FIELDNAME);
  $author->save();

  return $this->redirect('/autor/show?id='.$author->getId());
}

Formularvalidierung

NOTE Die Features, die in diesem Abschnitt beschrieben werden, sind in Symfony 1.1 veraltet und funktionieren nur, wenn Sie das Plugin sfCompat10 eingebunden haben.

In Kapitel 6 haben Sie erfahren, wie Sie die validateXXX()-Methoden in der Action-Klasse verwenden, um die Request-Parameter zu validieren. Wenn sie jedoch diese Technik verwenden, um Formulareingaben zu validieren, wird das damit enden, dass Sie den gleichen Code immer und immer wieder schreiben. Symfony stellt Ihnen daher eine weitere Vorgehensweise zur Verfügung, um Formulare zu validieren. Dabei werden anstelle von PHP-Code in der Action-Klasse nur YAML-Dateien verwendet.

Um die Fähigkeiten der Formularvalidierung zu zeigen, wollen wir zunächst das Beispiel aus Listing 10-17 näher betrachten. Es ist ein klassisches Kontaktformular mit den Feldern name, email, alter und nachricht.

Listing 10-17 - Beispiel-Kontaktformular, in modules/contact/templates/indexSuccess.php

[php]
<?php echo form_tag('contact/send') ?>
  Name:      <?php echo input_tag('name') ?><br />
  Email:     <?php echo input_tag('email') ?><br />
  Alter:     <?php echo input_tag('alter') ?><br />
  Nachricht: <?php echo textarea_tag('nachricht') ?><br />
  <?php echo submit_tag() ?>
</form>

Der Formularvalidierung liegt zugrunde, dass sobald der Benutzer falsche Daten eingibt und das Fromular abschickt, eine Fehlermeldung auf der nächsten Seite angezeigt wird. Also lassen Sie uns einmal im Klartext aufschreiben, was in unserem Beispiel als "gültige Daten" gilt:

  • Das Feld name ist eine Pflichtangabe. Es muss ein Text zwischen 2 und 100 Zeichen Länge eingegeben werden.
  • Das Feld email ist eine Pflichtangabe. Es muss ein Text zwischen 2 und 100 Zeichen Länge eingegeben werden, und es muss eine gültige Emailadresse sein.
  • Das Feld alter ist eine Pflichtangabe. Es muss eine Ganzzahl zwischen 0 und 120 sein.
  • Das Feld nachricht ist eine Pflichtangabe.

Sie können sicherlich noch weitere, komplexere Validierungsregeln für das Kontaktformular festlegen, aber diese hier reichen völlig für die Demonstration der Möglichkeiten.

NOTE Formularvalidierung kann sowohl auf Serverseite als auch auf Clientseite stattfinden. Eine serverseitige Validierung ist zwingend notwendig, um eine Verschmutzung der Datenbank mit fehlerhaften Daten zu vermeiden. Die clientseitige Validierung hingegen ist optional, verbessert jedoch die Qualität der Benutzerinteraktion. Clientseitige Validierung findet mit eigenem Javascript statt.

Validatoren

Wie Sie sicherlich bemerkt haben, haben die Felder name und email in unserem Beispiel gemeinsame Validierungsregeln. Einige dieser Regeln treten in Webformularen so häufig auf, dass symfony den dafür notwendigen PHP-Code in den sog. Validatoren bereitstellt. Ein Validator ist eine einfache Klasse, die eine Methode execute() bereitstellt. Diese Methode erwartet den Wert eines Formularfelds als Parameter und gibt wahr zurück, wenn der Wert gültig ist, ansonsten falsch.

Symfony beinhaltet unterschiedliche Validatoren (sie werden weiter unten im Abschnitt "Standard-Validatoren in Symfony" beschrieben), aber lassen Sie uns zunächst den sfStringValidator betrachten. Dieser Validator überprüft, ob die Eingabe eine Zeichenkette ist und ob ihre Länge zwischen zwei vorgegebenen Werten liegt (welche beim Aufruf der Methode initialize() angegeben werden). Dies ist genau das, was notwendig ist, um das Feld name zu validieren. Listing 10-18 zeigt zeigt, wie der Validator in einer Validierungsmethode verwendet wird.

Listing 10-18 - Validieren von Request-Parametern mit wiederverwendbaren Validatoren, in modules/contact/action/actions.class.php

[php]
public function validateSend()
{
  $name = $this->getRequestParameter('name');

  // Das Feld `name` ist obligatorisch
  if (!$name)
  {
    $this->getRequest()->setError('name', 'Der Name darf nicht leer sein');

    return false;
  }

  // Das Feld `name` muss ein Text mit 2 bis 100 Zeichen Länge sein
  $myValidator = new sfStringValidator($this->getContext(), array(
    'min'       => 2,
    'min_error' => 'Der Name ist zu kurz (mind. 2 Zeichen)',
    'max'       => 100,
    'max_error' => 'Der Name ist zu lang (max. 100 Zeichen)',
  ));
  if (!$myValidator->execute($name, $error))
  {
    return false;
  }

  return true;
}

Wenn ein Benutzer das Formular aus Listing 10-17 mit dem Wert a im Feld name abschickt, wird die Methode execute() des sfStringValidator den Wert falsch zurückgeben (weil die Zeichenanzahl weniger als der geforderte Mindestwert von 2 Zeichen ist). Die Methode validateSend() wird deshalb fehlschlagen, und somit wird die Methode handleErrorSend() anstelle von executeSend() aufgerufen.

TIP Die Methode setError() der Klasse sfRequest übergibt Informationen an das Template, damit es eine Fehlermeldung anzeigen kann (mehr dazu später in diesem Kapitel im Abschnitt "Fehlermeldungen im Formular anzeigen"). Die Validatoren setzen diese Fehler intern, so dass sie verschiedene Fehlermeldungen für verschiedene ungültige Eingaben definieren können. Das ist der Sinn hinter den Parametern min_error und max_error, die sie während der Initialisierung des sfStringValidator übergeben.

Alle Regeln, die im Beispiel verwendet werden, können in Validatoren umgesetzt werden:

  • name: sfStringValidator (min=2, max=100)
  • email: sfStringValidator (min=2, max=100) und sfEmailValidator
  • alter: sfNumberValidator (min=0, max=120)

Die Tatsache, dass ein Feldwert obligatorisch ist, wird allerdings nicht über einen Validator geprüft.

Validierungsdatei

Sie könnten nun die Validierung des Kontaktformulars ganz einfach über die Validatoren in der Methode validateSend() implementieren - dies würde jedoch zu einer Menge identischen Codes führen. Symfony bietet Ihnen daher einen alternativen Weg an, um Validierungsregeln für ein Formular zu definieren, und verwendet dazu YAML. Listing 10-19 zeigt die Umsetzung der Validierungsregeln des Felds name, wobei die Resultate identisch zu denen aus Listing 10-18 sind.

Listing 10-19 - Validierungsdatei, in modules/contact/validate/send.yml

fields:
  name:
    required:
      msg:       Der Name darf nicht leer sein
    sfStringValidator:
      min:       2
      min_error: Der Name ist zu kurz (mind. 2 Zeichen)
      max:       100
      max_error: Der Name ist zu lang (max. 100 Zeichen)

Innerhalb einer Validierungsdatei wird unterhalb des Schlüssels fields für alle zu validierenden Felder aufgeführt, ob ihre Eingabe obligatorisch ist, und, wenn ein Wert vorhanden ist, welche Validatoren auf ihn angewendet werden sollen. Die Parameter von jedem Validator sind die gleichen wie diejenigen, die Sie verwenden würden, wenn Sie den Validator im Code initialisieren. Jedes Feld kann von beliebig vielen Validatoren überprüft werden.

NOTE Der Validierungsvorgang stoppt nicht, wenn eine der Validierungen fehlschlägt. Symfony testet alle Validatoren, und die Validierung gilt als fehlgeschlagen, wenn einer davon fehlschlägt. Selbst wenn einige der Regeln aus der Validierungsdatei fehlschlagen, sucht symfony dennoch nach einer Methode validateXXX() und führt diese aus. Beide Validierungsarten ergänzen sich also. Der Vorteil dieser Vorgehensweise ist, dass in einem Formular mit mehreren fehlerhaften Eingaben alle Fehlermeldungen angezeigt werden.

Validierungsdateien werden im Verzeichnis validate/ des Modules gespeichert und heißen wie die Action, die sie validieren sollen. Die Validierungsdatei aus Listing 10-19 muss demnach in einer Datei validate/send.yml gespeichert werden.

Das Formular erneut anzeigen

Standardmäßig schaut symfony nach der Methode handleErrorSend() in der Action-Klasse, sobald der Validierungsprozess fehlschlägt, oder zeigt das Template sendError.php an, wenn es diese Methode nicht gibt.

Der übliche Weg, den Benutzer über eine fehlgeschlagene Validierung zu informieren, ist, das Formular mit einer Fehlermeldung erneut anzuzeigen. Zu diesem Zweck müssen Sie die Methode handleErrorSend() überschreiben und sie mit einem Redirekt auf die Action, die das Formular anzeigt (im Beispiel also module/index), beenden, wie in Listing 10-20 gezeigt.

Listing 10-20 - Das Formular erneut anzeigen, in modules/contact/actions/actions.class.php

[php]
class ContactActions extends sfActions
{
  public function executeIndex()
  {
    // Formular anzeigen
  }

  public function handleErrorSend()
  {
    $this->forward('contact', 'index');
  }

  public function executeSend()
  {
    // Formularübertragung bearbeiten
  }
}

Wenn Sie die gleiche Action verwenden, um das Formular anzuzeigen und die Formularübertragung zu bearbeiten, dann kann die Methode handleErrorSend() einfach den Wert sfView::SUCCESS zurückgeben, damit das Formular aus sendSuccess.php wieder angezeigt wird, wie in Listing 10-21 gezeigt.

Listing 10-21 - Nur eine Action zum Anzeigen und Verarbeiten des Formulars, in modules/contact/actions/actions.class.php

[php]
class ContactActions extends sfActions
{
  public function executeSend()
  {
    if (!$this->getRequest()->isMethod('post'))
    {
      // Daten für das Template vorbereiten

      // Formular anzeigen
      return sfView::SUCCESS;
    }
    else
    {
      // Formularübertragung bearbeiten
      ...
      $this->redirect('mymodule/weitereaction');
    }
  }
  public function handleErrorSend()
  {
    // Daten für das Template vorbereiten

    // Formular anzeigen
    return sfView::SUCCESS;
  }
}

Die Programmlogik, die die Daten für das Template vorbereitet, kann in eine eigene geschützte (protected) Methode innerhalb der Action-Klasse verschoben werden, damit der Code in den Methoden executeSend() und handleErrorSend() nicht wiederholt werden muss.

Mit dieser neuen Konfiguration wird das Formular wieder angezeigt, wenn der Benutzer einen ungültigen Namen eingibt, aber die eingegebenen Daten sind verloren und es wird keine Meldung über die Ursache des Fehlers angezeigt. Um letzteres zu beheben, müssen Sie das Template des Formulars ändern und Fehlermeldungen in der Nähe des fehlerhaften Felds einfügen.

Fehlermeldungen im Formular anzeigen

Die Fehlermeldungen, die dem Validator als Parameter übergeben worden sind, werden zum Request hinzugefügt, wenn die Validierung für ein Feld fehlschlägt (genauso können Sie natürlich eine Fehlermeldung mit der Methode setError() manuell hinzufügen, siehe Listing 10-18). Das sfRequest-Objekt stellt zwei nützliche Methoden zur Verfügung, um Fehlermeldungen zu ermitteln: hasError() und getError(), die jeweils den Namen eines Felds als Parameter erwarten. Zusätzlich können Sie mit der Methode hasErrors() eine allgemeine Warnung über dem Formular einblenden, um dem Benutzer anzuzeigen, dass ein oder mehrere Felder fehlerhafte Daten enthalten. Listing 10-22 und 10-23 zeigen, wie man diese Methoden verwendet.

Listing 10-22 - Fehlermeldungen über dem Formular anzeigen, in templates/indexSuccess.php

[php]
<?php if ($sf_request->hasErrors()): ?>
  <p>Ihre Eingaben sind ungültig.
  Bitte korrigieren Sie folgende Fehler und schicken Sie das Formular nochmals ab:</p>
  <ul>
  <?php foreach($sf_request->getErrors() as $name => $error): ?>
    <li><?php echo $name ?>: <?php echo $error ?></li>
  <?php endforeach; ?>
  </ul>
<?php endif; ?>

Listing 10-23 - Fehlermeldungen innerhalb des Formulars anzeigen, in templates/indexSuccess.php

[php]
<?php echo form_tag('contact/send') ?>
  <?php if ($sf_request->hasError('name')): ?>
    <?php echo $sf_request->getError('name') ?> <br />
  <?php endif; ?>
  Name:    <?php echo input_tag('name') ?><br />
  ...
  <?php echo submit_tag() ?>
</form>

Die bedingte Verwendung von getError() in Listing 10-23 ist umständlich zu schreiben. Deshalb bietet symfony als Ersatz den Helfer form_error() an, vorausgesetzt, sie haben die Helfergruppe Validation eingebunden. Listing 10-24 ersetzt Listing 10-23, indem es diesen Helfer verwendet.

Listing 10-24 - Fehlermeldungen innerhalb des Formulars anzeigen (der kurze Weg)

[php]
<?php use_helper('Validation') ?>
<?php echo form_tag('contact/send') ?>

           <?php echo form_error('name') ?><br />
  Name:    <?php echo input_tag('name') ?><br />
  ...
  <?php echo submit_tag() ?>
</form>

Der form_error()-Helfer fügt spezielle Zeichen vor und nach jeder Fehlermeldung ein, um die Meldungen deutlicher hervorzuheben. Standardmäßig ist dieses Zeichen ein nach unten gerichteter Pfeil (entspricht der HTML-Entity &darr;), aber Sie können dies in der settings.yml jederzeit ändern:

all:
  .settings:
    validation_error_prefix:    ' &darr;&nbsp;'
    validation_error_suffix:    ' &nbsp;&darr;'

Wenn die Validierung fehlschlägt, zeigt unser Formular nun also korrekte Fehlermeldungen an, aber die vom Benutzer eingegebenen Daten sind verloren. Sie müssen die Eingaben wieder im Formular anzeigen, um es wirklich benutzerfreundlich zu machen.

Daten im Formular wieder anzeigen (Wiederbefüllung)

Da die Fehlerbehandlung mit der Methode forward() durchgeführt wird (siehe Listing 10-20), können wir immer noch auf den ursprünglichen Request zugreifen, und die Daten, welche der User eingegeben hat, sind als Requestparameter verfügbar. Somit könnten Sie das Formular wiederbefüllen, indem Sie jedem Feld Defaultwerte übergeben, wie in Listing 10-25 gezeigt.

Listing 10-25 - Wiederbefüllung der Formularfelder über Defaultwerte, wenn die Validierung fehlschlägt, in templates/indexSuccess.php

[php]
<?php use_helper('Validation') ?>
<?php echo form_tag('contact/send') ?>
             <?php echo form_error('name') ?><br />
  Name:      <?php echo input_tag('name', $sf_params->get('name')) ?><br />
             <?php echo form_error('email') ?><br />
  Email:     <?php echo input_tag('email', $sf_params->get('email')) ?><br />
             <?php echo form_error('alter') ?><br />
  Alter:     <?php echo input_tag('alter', $sf_params->get('alter')) ?><br />
             <?php echo form_error('nachricht') ?><br />
  Nachricht: <?php echo textarea_tag('nachricht', $sf_params->get('nachricht')) ?><br />
  <?php echo submit_tag() ?>
</form>

Aber wie zuvor ist das eine sehr mühsame Angelegenheit. Symfony stellt daher für die Wiederbefüllung einen alternativen Weg zur Verfügung, und zwar direkt aus der YAML-Validierungsdatei heraus, ohne die Defaultwerte der Elemente zu ändern. Benutzen Sie dafür einfach das fillin:-Feature für das Formular. Der Syntax wird in Listing 10-26 beschrieben.

Listing 10-26 - fillin zur Wiederbefüllung verwenden, wenn die Validierung fehlgeschlagen ist, in validate/send.yml

fillin:
  enabled: true  # Wiederbefüllung aktivieren
  param:
    name: test  # Name des Formulars (nicht notwendig, wenn nur ein Formular auf der Seite ist)
    skip_fields:   [email]  # Diese Felder nicht wiederbefüllen
    exclude_types: [hidden, password] # Diese Feldarten nicht wiederbefüllen
    check_types:   [text, checkbox, radio, password, hidden] # Diese Feldarten wiederbefüllen

Standardmäßig funktioniert die automatische Wiederbefüllung für Textfelder, Checkboxen, Radiobuttons, mehrzeilige Textfelder (Textareas) und Auswahlfelder (Select, sowohl bei Dropdowns als auch bei Listen), aber es befüllt keine Passwort- und keine versteckten Formularfelder. Außerdem funktioniert das fillin-Feature nicht bei Feldern zum Hochladen von Dateien.

NOTE Das fillin-Feature analysiert die HTML-Antwort im Response mit einem XML-Parser, bevor diese zum Benutzer geschickt wird. Wenn die HTML-Antwort daher kein gültiges XHTML-Dokument ist, funktioniert fillin möglicherweise nicht korrekt.

Möglicherweise wollen Sie die Benutzereingaben umformatieren, bevor Sie sie als Formulareingabe zurückschicken. Escapen, URL-Rewriting, Transformation von Sonderzeichen in HTML-Entities, und alle anderen Transformationen, die durch einen Funktionsaufruf durchgeführt werden können, können Sie auf die Felder Ihres Formulars anwenden, indem Sie die Transformation unter dem Schlüssel converters: definieren, wie in Listing 10-27 gezeigt.

Listing 10-27 - Eingaben vor dem fillin konvertieren, in validate/send.yml

fillin:
  enabled: true
  param:
    name: test
    converters:         # Anzuwendende Transformationen
      htmlentities:     [first_name, comments]
      htmlspecialchars: [comments]

Standard-Validatoren in Symfony

Symfony bringt bereits einige Standard-Validatoren mit, die Sie für Ihre Formulare verwenden können:

  • sfStringValidator
  • sfNumberValidator
  • sfEmailValidator
  • sfUrlValidator
  • sfRegexValidator
  • sfCompareValidator
  • sfPropelUniqueValidator
  • sfFileValidator
  • sfCallbackValidator

Jeder dieser Validatoren hat Standardvorgaben für Parameter und Fehlermeldungem, die Sie allerdings einfach durch die Methode initialize() oder in der YAML-Datei verändern können. Der folgende Abschnitt beschreibt die Standard-Validatoren und zeigt Anwendungsbeispiele.

Zeichenketten-Validator

sfStringValidator ermöglicht Beschränkung auf Zeichenketten (Strings).

sfStringValidator:
  values:       [foo, bar]
  values_error: Die einzigen akzeptierten Werte sind foo und bar
  insensitive:  false  # Wenn true, dann ist der Vergleich mit den Werten aus `values` unabhängig von Groß- und Kleinschreibung
  min:          2
  min_error:    Bitte geben Sie mindestens 2 Zeichen ein
  max:          100
  max_error:    Bitte geben Sie höchstens 100 Zeichen ein

Ziffern-Validator

sfNumberValidator überprüft, ob der Parameter eine Zahl ist und ermöglicht die Beschränkung auf Ober- und Untergrenzen.

sfNumberValidator:
  nan_error:    Bitte geben Sie eine Ganzzahl ein
  min:          0
  min_error:    Der Wert muss mindestens 0 sein
  max:          100
  max_error:    Der Wert darf höchstens 100 sein

E-Mail-Validator

sfEmailValidator überprüft, ob der Parameter eine Zeichenkette enthält, die eine Emailadresse darstellt.

sfEmailValidator:
  strict:       true
  email_error:  Die E-Mail-Adresse ist ungültig

Das Format von Emailadressen ist in RFC822 definiert. Es ist jedoch viel weiter gefasst als die üblicherweise verwendeten Formate. So wäre z.B. `me@localhosteine dem RFC nach gültige E-Mail-Adresse, die Sie vermutlich eher nicht akzeptieren würden. Wenn der Parameterstrictauftruegesetzt ist (das ist die Vorgabe), dann werden nur Emailadressen akzeptiert, die dem Mustername@domain.erweiterung? entsprechen. Wenn der Wert auf false gesetzt ist, gelten die Maßgaben aus RFC822.

URL-Validator

sfUrlValidator prüft, ob ein Feld eine gültige URL enthält.

sfUrlValidator:
  url_error:    Diese URL ist ungültig

Validator für reguläre Ausdrücke (Regular Expressions)

sfRegexValidator ermöglicht es Ihnen, einen Wert gegen einen Perl-kompatiblen regulären Ausdruck zu prüfen.

sfRegexValidator:
  match:        No
  match_error:  Beiträge, die mehr als eine URL enthalten, werden als Spam betrachtet
  pattern:      /http.*http/si

Der Parameter match gibt an, ob der zu prüfende Wert auf den Ausdruck passen muss (Yes) oder nicht passen soll (No).

Vergleichsvalidator

sfCompareValidator vergleicht zwei verschiedene Requestparameter. Nützlich ist er z.B. beim Überprüfen von Passwörtern.

fields:
  password1:
    required:
      msg:      Bitte geben Sie ein Passwort an
  password2:
    required:
      msg:      Bitte wiederholen Sie das Passwort
    sfCompareValidator:
      check:    password1
      compare_error: Die beiden Passworteingaben sind nicht identisch

Der Parameter check enthält den Namen des Felds, mit dem das aktuelle Feld identisch sein muss, damit die Prüfung erfolgreich ist.

Standardmäßig überprüft der Validator die Gleichheit der Werte. Sie können dieses Verhalten ändern, indem Sie den Parameter operator angeben. Als Operatoren stehen zur Verfügung: >, >=, <, <=, == and !=.

Eindeutigkeits-Validator für Propel

sfPropelUniqueValidator überprüft, ob der Wert eines Requestparameters bislang noch nicht in der Datenbank existiert. Er kann sich bei eindeutigen Indizes als nützlich erweisen.

fields:
  nickname:
    sfPropelUniqueValidator:
      class:        User
      column:       login
      unique_error: Dieser Login existiert bereits. Bitte geben Sie einen anderen an.

In diesem Beispiel überprüft der Validator, ob in der Datenbank für die Klasse User ein Datensatz existiert, bei dem die Spalte login den gleichen Wert wie das zu prüfende Feld hat.

ACHTUNG Der sfPropelUniqueValidator ist anfällig für Race Conditions (Nebenläufigkeitsprobleme). Auch wenn es eher unwahrscheinlich ist, könnte sich in einer Mehrbenutzer-Umgebung das Ergebnis schon geändert haben, sobald es zurückgeben wurde. Aus diesem Grunde sollten Sie trotz des Validators Fehler beim INSERT entsprechend abfangen und behandeln.

Datei-Validator

sfFileValidator überprüft eine hochgeladene Datei auf Format (bestimmte Mime-Typen) und Größe.

fields:
  image:
    file:       True
    required:
      msg:      Bitte laden Sie eine Bilddatei hoch
    sfFileValidator:
      mime_types:
        - 'image/jpeg'
        - 'image/png'
        - 'image/x-png'
        - 'image/pjpeg'
      mime_types_error: Es sind nur JPEG- und PNG-Bilder erlaubt
      max_size:         512000
      max_size_error:   Die maximale Dateigröße ist 512Kb

Beachten Sie bitte, dass der Parameter file auf true gesetzt sein muss, und dass das Formular im Template als Multipart gekennzeichnet werden muss.

Callback-Validator

sfCallbackValidator delegiert die Validierung zu einer Methode oder Funktion eines Drittanbieters, welche dann die Validierung durchführt. Die aufrufbare Methode oder Funktion muss true oder false zurückgeben.

fields:
  account_number:
    sfCallbackValidator:
      callback:      is_numeric
      invalid_error: Bitte geben Sie eine Zahl ein.
  credit_card_number:
    sfCallbackValidator:
      callback:      [myTools, validateCreditCard]
      invalid_error: Bitte geben Sie ein gültige Kreditkartennummer ein.

Die Callbackmethode oder -Funktion erhält den zu prüfenden Wert als ersten Parameter übergeben. Das ist nützlich, wenn Sie existierende Methoden oder Funktionen verwenden wollen, anstatt eine komplette Validator-Klasse zu erstellen.

TIP Wie Sie Ihre eigenen Validatoren schreiben, wird im Abschnitt "Eigene Validatoren erstellen" später in diesem Kapitel erklärt.

Benannte Validatoren

Wenn Sie feststellen, dass Sie eine Validatorenklasse und deren Einstellungen wiederholt verwenden müssen, dann können Sie daraus einen Benannten Validator machen. Im Kontaktformular aus dem obigen Beispiel hat das Feld email die gleichen Einstellungen für den sfStringValidator wie das Feld name. Somit können Sie einen Benannten Validator namens meinStringValidator erzeugen, damit Sie die gleichen Einstellungen nicht zweimal angeben müssen. Um dies zu erreichen, fügen Sie im Abschnitt validators ein Feld meinStringValidator ein und füllen die Schlüssel class und param mit den Details des Validators, den Sie benennen wollen. Den Benannten Validator können Sie dann im Abschnitt fields wie normale verwenden, wie in Listing 10-28 gezeigt.

Listing 10-28 - Wiederverwendung von Benannten Validatoren in einer Validierungsdatei, in validate/send.yml

validators:
  meinStringValidator:
    class: sfStringValidator
    param:
      min:       2
      min_error: Ihre Eingabe war zu kurz (mind. 2 Zeichen)
      max:       100
      max_error: Ihre Eingabe war zu lang (max. 100 Zeichen)

fields:
  name:
    required:
      msg:       Der Name darf nicht leer sein
    meinStringValidator:
  email:
    required:
      msg:       Die Email darf nicht leer sein
    meinStringValidator:
    sfEmailValidator:
      email_error:  Diese Emailadresse ist ungültig

Beschränkung der Validierung auf eine bestimmte HTTP-Methode

Standardmäßig werden die Validatoren aus der Validierungsdatei nur aufgerufen, wenn die Action mit der POST-Methode aufgerufen wird. Sie können diese Einstellung global ändern oder feldweise, indem Sie einen Wert im Schlüssel methods angeben, um unterschiedliche Validierungen für unterschiedliche Methoden zu ermöglichen, wie in Listing 10-29 gezeigt.

Listing 10-29 - Festlegen, wann ein Feld getestet wird, in validate/send.yml

methods:         [post]     # Das ist die globale Einstellung

fields:
  name:
    required:
      msg:       Der Name darf nicht leer sein
    myStringValidator:
  email:
    methods:     [post, get] # Überschreibt die globale Einstellunge
    required:
      msg:       Die Email darf nicht leer sein
    myStringValidator:
    sfEmailValidator:
      email_error:  Diese Emailadresse ist ungültig

Wie sieht eine Validierungsdatei aus?

Bisher haben Sie nur kleine Ausschnitte aus einer Validierungsdatei gesehen. Wenn Sie nun alles zusammensetzen, dann haben die Validierungsregeln eine direkte Übersetzung in YAML. Listing 10-30 zeigt die komplette Validierungsdatei für das Kontaktformular aus unserem Beispiel, mit allen Regeln, die wir weiter oben festgelegt haben.

Listing 10-30 - Beispiel einer Kompletten Validierungsdatei

fillin:
  enabled:      true

validators:
  meinStringValidator:
    class: sfStringValidator
    param:
      min:       2
      min_error: Ihre Eingabe war zu kurz (mind. 2 Zeichen)
      max:       100
      max_error: Ihre Eingabe war zu lang (max. 100 Zeichen)

fields:
  name:
    required:
      msg:          Der Name darf nicht leer sein
    meinStringValidator:
  email:
    required:
      msg:          Die Email darf nicht leer sein
    meinStringValidator:
    sfEmailValidator:
      email_error:  Diese Emailadresse ist ungültig
  alter:
    sfNumberValidator:
      nan_error:    Bitte geben Sie eine Ganzzahl ein
      min:          0
      min_error:    "Sie sind noch nicht einmal geboren... wie wollen Sie da eine Nachricht schreiben?"
      max:          120
      max_error:    "Na, Oma, bist du nicht schon zu alt fürs Surfen?"
  nachricht:
    required:
      msg:          Die Nachricht darf nicht leer sein

Komplexe Validierung

Die Möglichkeiten der Validierungsdatei dürfte den meisten Anforderungen genügen, aber sie könnten nicht ausreichen, wenn die Validierung sehr komplex ist. In diesen Fällen könnten Sie immer noch auf die validateXXX()-Methode in der Action zurückgreifen, oder Sie finden eine Lösung für Ihr Problem in den folgenden Abschnitten.

Eigene Validatoren erstellen

Jeder Validator besteht aus einer Klasse, die von der Klasse sfValidator abgeleitet ist. Wenn die Validatoren von symfony Ihren Anforderungen nicht ausreichen, können Sie ganz einfach einen neuen schreiben, den Sie in einem der lib/-Verzeichnisse ablegen, aus welchem er automatisch geladen werden kann. Der Syntax ist recht einfach: Die Methode execute() des Validators wird aufgerufen, wenn der Validator ausgeführt wird. In der Methode initialize() können Sie Standardeinstellungen festlegen.

Die Methode execute() erhält den zu validierenen Wert als ersten Parameter und die Fehlermeldung als zweiten. Beide werden als Referenz übergeben, so dass Sie die Fehlermeldung aus der Methode heraus verändern können.

Die Methode initialize() erhält das Context-Singleton und ein Array mit Parametern aus der YAML-Datei. Sie muss zunächst die Elternmethode aus der Klasse sfValidator aufrufen, und dann die Defaultwerte setzen.

Jeder Validator hat einen Parameterholder, der über $this->getParameterHolder() erreichbar ist.

Wenn Sie beispielsweise einen sfSpamValidator erstellen wollen, der prüft, ob ein String Spam ist oder nicht, erstellen Sie die Datei sfSpamValidator.class.php mit dem Code aus Listing 10-31. Er prüft, ob der $value die Zeichenfolge http mehr als max_url Mal enthält.

Listing 10-31 - Erstellung eines eigenen Validators, in lib/sfSpamValidator.class.php

[php]
class sfSpamValidator extends sfValidator
{
  public function execute (&$value, &$error)
  {
    // Bei max_url=2 ist der reguläre Ausdruck /http.*http/is
    $re = '/'.implode('.*', array_fill(0, $this->getParameter('max_url') + 1, 'http')).'/is';

    if (preg_match($re, $value))
    {
      $error = $this->getParameter('spam_error');

      return false;
    }

    return true;
  }

  public function initialize ($context, $parameters = null)
  {
    // Aufruf der Elternmethode
    parent::initialize($context);

    // Defaultwerte der Parameter setzen
    $this->setParameter('max_url', 2);
    $this->setParameter('spam_error', 'This is spam');

    // Die Parameter übergeben
    $this->getParameterHolder()->add($parameters);

    return true;
  }
}

Sobald Sie den Validator in ein Verzeichnis gespeichert haben, das vom Autoload-Feature erfasst wird, und den Cache geleert haben, können Sie ihn in Ihren Validierungsdateien verwenden, wie Listing 10-32 zeigt.

Listing 10-32 - Einsatz eines eigenen Validators, in validate/send.yml

fields:
  nachricht:
    required:
      msg:          Die Nachricht darf nicht leer sein
    sfSpamValidator:
      max_url:      3
      spam_error:   Hau bloß ab, du dreckiger Spammer!

Verwenden von Array-Syntax für Formularfelder

PHP gestattet Ihnen, für die Benennung von Formularfeldern auch einen Array-Syntax zu verwenden. Wenn Sie Ihre eigenen Formular erstellen oder die verwenden, die mit den Administrationstasks von Propel automatisch erzeugt wurden (siehe Kapitel 14), werden Sie am Ende vermutlich HTML-Code haben, der aussieht wie Listing 10-33.

Listing 10-33 - Formulare mit Array-Syntax

[php]
<label for="story_titel">Titel:</label>
<input type="text" name="story[titel]" id="story_titel" value="Standardwert"
       size="45" />

Wenn Sie den Feldnamen wie beschrieben mit den eckigen Klammern in einer Validierungsdatei verwenden, führt das zu einem Parserfehler. Daher müssen wir im Abschnitt fields die eckigen Klammern [] durch geschweifte Klammern {} ersetzen, wie in Listing 10-34 gezeigt. Symfony sorgt dann automatisch für die Korrektur der Namen, die dann zum Validator weitergereicht werden.

Listing 10-34 - Validierungsdatei für ein Formular mit Array-Syntax

fields:
  story{titel}:
    required:     Yes

Einen Validator auf einem leeren Feld ausführen

Es kann vorkommen, dass Sie einen Validator auf einem optionalen Feld ausführen, das einen leeren Wert enthält. Dies könnte beispielsweise ein Formular sein, auf dem der User sein Passwort ändern kann (jedoch nicht muss); in diesem Fall muss dann allerdings auch eine Passwortbestätigung eingegeben sein. Listing 10-35 zeigt dieses Beispiel.

Listing 10-35 - Validierungsdatei für ein Formular mit zwei Passwortfeldern

fields:
  passwort1:
  passwort2:
    sfCompareValidator:
      check:         passwort1
      compare_error: Die beiden Passworteingaben sind nicht identisch.

Der Validierungsvorgang läuft hier wie folgt ab:

  • Wenn passwort1 == null und passwort2 == null:

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • Die Validatoren werden nicht gestartet.
    • Das Formular ist gültig.
  • Wenn passwort2 == null, während passwort1 nicht null ist:

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • Die Validatoren werden nicht gestartet.
    • Das Formular ist gültig.

Sie wollen möglicherweise erreichen, dass der Validator für passwort2 ausgeführt wird, wenn passwort1 nicht null ist. Zum Glück können die Validatoren von symfony mit diesem Fall umgehen, dank des Parameters group. Wenn ein Feld Mitglied einer Gruppe ist, dann wird sein Validator ausgeführt, wenn es nicht leer ist und mindestens ein weiteres Feld der gleichen Gruppe ebenfalls nicht leer ist.

Wenn Sie somit die Konfiguration so ändern wie in Listing 10-36 gezeigt, verläuft der Validierungsprozess korrekt.

Listing 10-35 - Validierungsdatei für ein Formular mit zwei Passwortfeldern unter Verwendung einer Gruppe

fields:c
  passwort1:
    group:           passwort_gruppe
  passwort2:
    group:           passwort_gruppe
    sfCompareValidator:
      check:         passwort1
      compare_error: Die beiden Passworteingaben sind nicht identisch.

Diesmal läuft der Validierungsvorgang folgendermaßen ab:

  • Wenn passwort1 == null und passwort2 == null:

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • Die Validatoren werden nicht gestartet.
    • Das Formular ist gültig.
  • Wenn passwort1 == null und passwort2 == 'foo':

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • passwort2 ist nicht null, daher wird der Validator ausgeführt und schlägt fehl.
    • Eine Fehlermeldung wird für passwort2 erzeugt.
  • Wenn passwort1 == 'foo' und passwort2 == null:

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • passwort1 ist nicht null, daher wird der Validator für passwort2, der in der gleichen Gruppe ist, auf jeden Fall auch ausgeführt, und schlägt fehl.
    • Eine Fehlermeldung wird für passwort2 erzeugt.
  • Wenn passwort1 == 'foo' und passwort2 == 'foo':

    • Der Test, ob ein Feldinhalt vorhanden ist (required), wird durchlaufen.
    • passwort2 ist nicht null, daher wird der Validator ausgeführt und ist erfolgreich.
    • Das Formular ist gültig.

Zusammenfassung

In symfony Formulare zu schreiben wird durch die Standard-Formular-Helfer und ihre praktischen Optionen erleichtert. Wenn Sie ein Formular erstellen, um die Eigenschaften eines Objekts zu ändern, dann vereinfachen die Objekt-Formular-Helfer diese Aufgabe erheblich. Validierungsdateien, Validierungs-Helfer und die Wiederbefüllung reduzieren den Aufwand, der notwendig ist, um robuste und nutzerfreundliche serverseitige Überprüfung von Feldern durchzuführen. Und selbst die komplexesten Validierungen lassen sich durchführen, indem Sie entweder einen eigenen Validator schreiben oder eine validateXXX()-Methode in der Actionklasse erstellen.