[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/doc/ -> 10-Forms.txt (source)

   1  Chapter 10 - Forms
   2  ==================
   3  
   4  When writing templates, much of a developer's time is devoted to forms. Despite this, forms are generally poorly designed. Since much attention is required to deal with default values, formatting, validation, repopulation, and form handling in general, some developers tend to skim over some important details in the process. Accordingly, symfony devotes special attention to this topic. This chapter describes the tools that automate many of these requirements while speeding up forms development:
   5  
   6    * The form helpers provide a faster way to write form inputs in templates, especially for complex elements such as dates, drop-down lists, and rich text.
   7    * When a form is devoted to editing the properties of an object, the templating can be further accelerated by using object form helpers.
   8    * The YAML validation files facilitate form validation and repopulation.
   9    * Validators package the code required to validate input. Symfony bundles validators for the most common needs, and it is very easy to add custom validators.
  10  
  11  Form Helpers
  12  ------------
  13  
  14  In templates, HTML tags of form elements are very often mixed with PHP code. Form helpers in symfony aim to simplify this task and to avoid opening `<?php echo` tags repeatedly in the middle of `<input>` tags.
  15  
  16  ### Main Form Tag
  17  
  18  As explained in the previous chapter, you must use the `form_tag()` helper to create a form, since it transforms the action given as a parameter into a routed URL. The second argument can support additional options--for instance, to change the default `method`, change the default `enctype`, or specify other attributes. Listing 10-1 shows examples.
  19  
  20  Listing 10-1 - The `form_tag()` Helper
  21  
  22      [php]
  23      <?php echo form_tag('test/save') ?>
  24       => <form method="post" action="/path/to/save">
  25  
  26      <?php echo form_tag('test/save', 'method=get multipart=true class=simpleForm') ?>
  27       => <form method="get" enctype="multipart/form-data" class="simpleForm"action="/path/to/save">
  28  
  29  As there is no need for a closing form helper, you should use the HTML `</form>` tag, even if it doesn't look good in your source code.
  30  
  31  ### Standard Form Elements
  32  
  33  With form helpers, each element in a form is given an id attribute deduced from its name attribute by default. This is not the only useful convention. See Listing 10-2 for a full list of standard form helpers and their options.
  34  
  35  Listing 10-2 - Standard Form Helpers Syntax
  36  
  37      [php]
  38      // Text field (input)
  39      <?php echo input_tag('name', 'default value') ?>
  40       => <input type="text" name="name" id="name" value="default value" />
  41  
  42      // All form helpers accept an additional options parameter
  43      // It allows you to add custom attributes to the generated tag
  44      <?php echo input_tag('name', 'default value', 'maxlength=20') ?>
  45       => <input type="text" name="name" id="name" value="default value"maxlength="20" />
  46  
  47      // Long text field (text area)
  48      <?php echo textarea_tag('name', 'default content', 'size=10x20') ?>
  49       => <textarea name="name" id="name" cols="10" rows="20">
  50            default content
  51          </textarea>
  52  
  53      // Check box
  54      <?php echo checkbox_tag('single', 1, true) ?>
  55      <?php echo checkbox_tag('driverslicense', 'B', false) ?>
  56       => <input type="checkbox" name="single" id="single" value="1"checked="checked" />
  57          <input type="checkbox" name="driverslicense" id="driverslicense"value="B" />
  58  
  59      // Radio button
  60      <?php echo radiobutton_tag('status[]', 'value1', true) ?>
  61      <?php echo radiobutton_tag('status[]', 'value2', false) ?>
  62       => <input type="radio" name="status[]" id="status_value1" value="value1"checked="checked" />
  63          <input type="radio" name="status[]" id="status_value2" value="value2" />
  64  
  65      // Dropdown list (select)
  66      <?php echo select_tag('payment',
  67        '<option selected="selected">Visa</option>
  68         <option>Eurocard</option>
  69         <option>Mastercard</option>')
  70      ?>
  71       => <select name="payment" id="payment">
  72            <option selected="selected">Visa</option>
  73            <option>Eurocard</option>
  74            <option>Mastercard</option>
  75          </select>
  76  
  77      // List of options for a select tag
  78      <?php echo options_for_select(array('Visa', 'Eurocard', 'Mastercard'), 0) ?>
  79       => <option value="0" selected="selected">Visa</option>
  80          <option value="1">Eurocard</option>
  81          <option value="2">Mastercard</option>
  82  
  83      // Dropdown helper combined with a list of options
  84      <?php echo select_tag('payment', options_for_select(array(
  85        'Visa',
  86        'Eurocard',
  87        'Mastercard'
  88      ), 0)) ?>
  89       => <select name="payment" id="payment">
  90            <option value="0" selected="selected">Visa</option>
  91            <option value="1">Eurocard</option>
  92            <option value="2">Mastercard</option>
  93          </select>
  94  
  95      // To specify option names, use an associative array
  96      <?php echo select_tag('name', options_for_select(array(
  97        'Steve'  => 'Steve',
  98        'Bob'    => 'Bob',
  99        'Albert' => 'Albert',
 100        'Ian'    => 'Ian',
 101        'Buck'   => 'Buck'
 102      ), 'Ian')) ?>
 103       => <select name="name" id="name">
 104            <option value="Steve">Steve</option>
 105            <option value="Bob">Bob</option>
 106            <option value="Albert">Albert</option>
 107            <option value="Ian" selected="selected">Ian</option>
 108            <option value="Buck">Buck</option>
 109          </select>
 110  
 111      // Dropdown list with multiple selection (selected values can be an array)
 112      <?php echo select_tag('payment', options_for_select(
 113        array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),
 114        array('Visa', 'Mastecard'),
 115      ), array('multiple' => true))) ?>
 116  
 117       => <select name="payment[]" id="payment" multiple="multiple">
 118            <option value="Visa" selected="selected">Visa</option>
 119            <option value="Eurocard">Eurocard</option>
 120            <option value="Mastercard">Mastercard</option>
 121          </select>
 122      // Drop-down list with multiple selection (selected values can be an array)
 123      <?php echo select_tag('payment', options_for_select(
 124        array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),
 125        array('Visa', 'Mastecard')
 126      ), 'multiple=multiple') ?>
 127       => <select name="payment" id="payment" multiple="multiple">
 128            <option value="Visa" selected="selected">    
 129            <option value="Eurocard">Eurocard</option>
 130            <option value="Mastercard" selected="selected">Mastercard</option>
 131          </select>
 132  
 133      // Upload file field
 134      <?php echo input_file_tag('name') ?>
 135       => <input type="file" name="name" id="name" value="" />
 136  
 137      // Password field
 138      <?php echo input_password_tag('name', 'value') ?>
 139       => <input type="password" name="name" id="name" value="value" />
 140  
 141      // Hidden field
 142      <?php echo input_hidden_tag('name', 'value') ?>
 143       => <input type="hidden" name="name" id="name" value="value" />
 144  
 145      // Submit button (as text)
 146      <?php echo submit_tag('Save') ?>
 147       => <input type="submit" name="submit" value="Save" />
 148  
 149      // Submit button (as image)
 150      <?php echo submit_image_tag('submit_img') ?>
 151       => <input type="image" name="submit" src="/images/submit_img.png" />
 152  
 153  The `submit_image_tag()` helper uses the same syntax and has the same advantages as the `image_tag()`.
 154  
 155  >**NOTE**
 156  >For radio buttons, the `id` attribute is not set by default to the value of the `name` attribute, but to a combination of the name and the value. That's because you need to have several radio button tags with the same name to obtain the automated "deselecting the previous one when selecting another" feature, and the `id=name` convention would imply having several HTML tags with the same `id` attribute in your page, which is strictly forbidden.
 157  
 158  -
 159  
 160  >**SIDEBAR**
 161  >Handling form submission
 162  >
 163  >How do you retrieve the data submitted by users through forms? It is available in the request parameters, so the action only needs to call `$this->getRequestParameter($elementName)` to get the value.
 164  >
 165  >A good practice is to use the same action to display and handle the form. According to the request method (GET or POST), either the form template is called or the form is handled and the request is redirected to another action.
 166  >
 167  >     [php]
 168  >     // In mymodule/actions/actions.class.php
 169  >     public function executeEditAuthor()
 170  >     {
 171  >       if ($this->getRequest()->getMethod() != sfRequest::POST)
 172  >       {
 173  >         // Display the form
 174  >         return sfView::SUCCESS;
 175  >       }
 176  >       else
 177  >       {
 178  >         // Handle the form submission
 179  >         $name = $this->getRequestParameter('name');
 180  >         ...
 181  >         $this->redirect('mymodule/anotheraction');
 182  >       }
 183  >     }
 184  >
 185  >For this to work, the form target must be the same action as the one displaying it.
 186  >
 187  >     [php]
 188  >     // In mymodule/templates/editAuthorSuccess.php
 189  >     <?php echo form_tag('mymodule/editAuthor') ?>
 190  >
 191  >     ...
 192  
 193  Symfony offers specialized form helpers to do asynchronous requests in the background. The next chapter, which focuses on Ajax, provides more details.
 194  
 195  ### Date Input Widgets
 196  
 197  Forms are often used to retrieve dates. Dates in the wrong format are the main reason for form-submission failures. The `input_date_tag()` helper can assist the user in entering a date with an interactive JavaScript calendar, if you set the `rich` option to `true`, as shown in Figure 10-1.
 198  
 199  Figure 10-1 - Rich date input tag
 200  
 201  ![Rich date input tag](/images/book/F1001.png "Rich date input tag")
 202  
 203  If the `rich` option is omitted, the helper echoes three `<select>` tags populated with a range of months, days, and years. You can display these drop-downs separately by calling their helpers (`select_day_tag()`, `select_month_tag()`, and `select_year_tag()`). The default values of these elements are the current day, month, and year. Listing 10-3 shows the input date helpers.
 204  
 205  Listing 10-3 - Input Date Helpers
 206  
 207      [php]
 208      <?php echo input_date_tag('dateofbirth', '2005-05-03', 'rich=true') ?>
 209       => a text input tag together with a calendar widget
 210  
 211      // The following helpers require the Date helper group
 212      <?php use_helper('Date') ?>
 213      <?php echo select_day_tag('day', 1, 'include_custom=Choose a day') ?>
 214      => <select name="day" id="day">
 215            <option value="">Choose a day</option>
 216            <option value="1" selected="selected">01</option>
 217            <option value="2">02</option>
 218            ...
 219            <option value="31">31</option>
 220          </select>
 221  
 222      <?php echo select_month_tag('month', 1, 'include_custom=Choose a month use_short_month=true') ?>
 223      => <select name="month" id="month">
 224            <option value="">Choose a month</option>
 225            <option value="1" selected="selected">Jan</option>
 226            <option value="2">Feb</option>
 227            ...
 228            <option value="12">Dec</option>
 229          </select>
 230  
 231      <?php echo select_year_tag('year', 2007, 'include_custom=Choose a year year_end=2010') ?>
 232       => <select name="year" id="year">
 233            <option value="">Choose a year</option>
 234            <option value="2006">2006</option>
 235            <option value="2007" selected="selected">2007</option>
 236            ...
 237          </select>
 238  
 239  The accepted date values for the `input_date_tag()` helper are the ones recognized by the `strtotime()` PHP function. Listing 10-4 shows which formats can be used, and Listing 10-5 shows the ones that must be avoided.
 240  
 241  Listing 10-4 - Accepted Date Formats in Date Helpers
 242  
 243      [php]
 244      // Work fine
 245      <?php echo input_date_tag('test', '2006-04-01', 'rich=true') ?>
 246      <?php echo input_date_tag('test', 1143884373, 'rich=true') ?>
 247      <?php echo input_date_tag('test', 'now', 'rich=true') ?>
 248      <?php echo input_date_tag('test', '23 October 2005', 'rich=true') ?>
 249      <?php echo input_date_tag('test', 'next tuesday', 'rich=true') ?>
 250      <?php echo input_date_tag('test', '1 week 2 days 4 hours 2 seconds', 'rich=true') ?>
 251  
 252      // Return null
 253      <?php echo input_date_tag('test', null, 'rich=true') ?>
 254      <?php echo input_date_tag('test', '', 'rich=true') ?>
 255  
 256  Listing 10-5 - Incorrect Date Formats in Date Helpers
 257  
 258      [php]
 259      // Date zero = 01/01/1970
 260      <?php echo input_date_tag('test', 0, 'rich=true') ?>
 261  
 262      // Non-English date formats don't work
 263      <?php echo input_date_tag('test', '01/04/2006', 'rich=true') ?>
 264  
 265  ### Rich Text Editing
 266  
 267  Rich text editing is also possible in a `<textarea>` tag, thanks to the integration of the TinyMCE and FCKEditor widgets. They provide a word-processor-like interface with buttons to format text as bold, italic, and other styles, as shown in Figure 10-2.
 268  
 269  Figure 10-2 - Rich text editing
 270  
 271  ![Rich text editing](/images/book/F1002.png "Rich text editing")
 272  
 273  Both widgets require manual installation. As the procedure is the same for the two widgets, only the TinyMCE rich text editing is described here. You need to download the editor from the project website ([http://tinymce.moxiecode.com/](http://tinymce.moxiecode.com/)) and unpack it in a temporary folder. Copy the `tinymce/jscripts/tiny_mce/` directory into your project `web/js/` directory, and define the path to the library in `settings.yml`, as shown in Listing 10-6.
 274  
 275  Listing 10-6 - Setting Up the TinyMCE Library Path
 276  
 277      all:
 278        .settings:
 279          rich_text_js_dir:  js/tiny_mce
 280  
 281  Once this is done, toggle the use of rich text editing in text areas by adding the `rich=true` option. You can also specify custom options for the JavaScript editor using the `tinymce_options` option. Listing 10-7 shows examples.
 282  
 283  Listing 10-7 - Rich Text Area
 284  
 285      [php]
 286      <?php echo textarea_tag('name', 'default content', 'rich=true size=10x20')) ?>
 287       => a rich text edit zone powered by TinyMCE
 288      <?php echo textarea_tag('name', 'default content', 'rich=true size=10x20tinymce_options=language:"fr",theme_advanced_buttons2:"separator"')) ?>
 289      => a rich text edit zone powered by TinyMCE with custom parameters
 290  
 291  ### Country and Language Selection
 292  
 293  You may need to display a country selection field. But since country names are not the same in all languages, the options of a country drop-down list should vary according to the user culture (see Chapter 13 for more information about cultures). As shown in Listing 10-8, the `select_country_tag()` helper does it all for you: It internationalizes country names and uses the standard ISO country codes for values.
 294  
 295  Listing 10-8 - Select Country Tag Helper
 296  
 297      [php]
 298      <?php echo select_country_tag('country', 'AL') ?>
 299       => <select name="country" id="country">
 300            <option value="AF">Afghanistan</option>
 301            <option value="AL" selected="selected">Albania</option>
 302            <option value="DZ">Algeria</option>
 303            <option value="AS">American Samoa</option>
 304        ...
 305  
 306  Similar to `select_country_tag()` helper, the `select_language_tag()` helper displays a list of languages, as shown in Listing 10-9.
 307  
 308  Listing 10-9 - Select Language Tag Helper
 309  
 310      [php]
 311      <?php echo select_language_tag('language', 'en') ?>
 312       => <select name="language" id="language">
 313            ...
 314            <option value="elx">Elamite</option>
 315            <option value="en" selected="selected">English</option>
 316            <option value="enm">English, Middle (1100-1500)</option>
 317            <option value="ang">English, Old (ca.450-1100)</option>
 318            <option value="myv">Erzya</option>
 319            <option value="eo">Esperanto</option>
 320            ...
 321  
 322  Form Helpers for Objects
 323  ------------------------
 324  
 325  When form elements are used to edit the properties of an object, standard link helpers can become tedious to write. For instance, to edit the `telephone` attribute of a `Customer` object, you would write this:
 326  
 327      [php]
 328      <?php echo input_tag('telephone', $customer->getTelephone()) ?>
 329      => <input type="text" name="telephone" id="telephone" value="0123456789" />
 330  
 331  To avoid repeating the attribute name, symfony provides an alternative object form helper for each form helper. An object form helper deduces the name and the default value of a form element from an object and a method name. The previous `input_tag()` is equivalent to this:
 332  
 333      [php]
 334      <?php echo object_input_tag($customer, 'getTelephone') ?>
 335      => <input type="text" name="telephone" id="telephone" value="0123456789" />
 336  
 337  The economy might not look crucial for the `object_input_tag()`. However, every standard form helper has a corresponding object form helper, and they all share the same syntax. It makes generation of forms quite straightforward. That's why the object form helpers are used extensively in the scaffolding and generated administrations (see Chapter 14). Listing 10-10 lists the object form helpers.
 338  
 339  Listing 10-10 - Object Form Helpers Syntax
 340  
 341      [php]
 342      <?php echo object_input_tag($object, $method, $options) ?>
 343      <?php echo object_input_date_tag($object, $method, $options) ?>
 344      <?php echo object_input_hidden_tag($object, $method, $options) ?>
 345      <?php echo object_textarea_tag($object, $method, $options) ?>
 346      <?php echo object_checkbox_tag($object, $method, $options) ?>
 347      <?php echo object_select_tag($object, $method, $options) ?>
 348      <?php echo object_select_country_tag($object, $method, $options) ?>
 349      <?php echo object_select_language_tag($object, $method, $options) ?>
 350  
 351  There is no `object_password_tag()` helper, since it is a bad practice to give a default value to a password tag, based on something the user has previously entered.
 352  
 353  >**CAUTION**
 354  >Unlike the regular form helpers, the object form helpers are available only if you declare explicitly the use of the `Object` helper group in your template with `use_helper('Object')`.
 355  
 356  The most interesting of all object form helpers are `objects_for_select()` and `object_select_tag()`, which concern drop-down lists.
 357  
 358  ### Populating Drop-Down Lists with Objects
 359  
 360  The `options_for_select()` helper, described previously with the other standard helpers, transforms a PHP associative array into an options list, as shown in Listing 10-11.
 361  
 362  Listing 10-11 - Creating a List of Options Based on an Array with `options_for_select()`
 363  
 364      [php]
 365      <?php echo options_for_select(array(
 366        '1' => 'Steve',
 367        '2' => 'Bob',
 368        '3' => 'Albert',
 369        '4' => 'Ian',
 370        '5' => 'Buck'
 371      ), 4) ?>
 372       => <option value="1">Steve</option>
 373          <option value="2">Bob</option>
 374          <option value="3">Albert</option>
 375          <option value="4" selected="selected">Ian</option>
 376          <option value="5">Buck</option>
 377  
 378  Suppose that you already have an array of objects of class `Author`, resulting from a Propel query. If you want to build a list of options based on this array, you will need to loop on it to retrieve the `id` and the `name` of each object, as shown in Listing 10-12.
 379  
 380  Listing 10-12 - Creating a List of Options Based on an Array of Objects with `options_for_select()`
 381  
 382      [php]
 383      // In the action
 384      $options = array();
 385      foreach ($authors as $author)
 386      {
 387        $options[$author->getId()] = $author->getName();
 388      }
 389      $this->options = $options;
 390  
 391      // In the template
 392      <?php echo options_for_select($options, 4) ?>
 393  
 394  This kind of processing happens so often that symfony has a helper to automate it: `objects_for_select()`, which creates an option list based directly on an array of objects. The helper needs two additional parameters: the method names used to retrieve the `value` and the text contents of the `<option>` tags to be generated. So Listing 10-12 is equivalent to this simpler form:
 395  
 396      [php]
 397      <?php echo objects_for_select($authors, 'getId', 'getName', 4) ?>
 398  
 399  That's smart and fast, but symfony goes even further, when you deal with foreign key columns.
 400  
 401  ### Creating a Drop-Down List Based on a Foreign Key Column
 402  
 403  The values a foreign key column can take are the primary key values of the foreign table records. If, for instance, the `article` table has an `author_id` column that is a foreign key to an `author` table, the possible values for this column are the `id` of all the records of the `author` table. Basically, a drop-down list to edit the author of an article would look like Listing 10-13.
 404  
 405  Listing 10-13 - Creating a List of Options Based on a Foreign Key with `objects_for_select()`
 406  
 407      [php]
 408      <?php echo select_tag('author_id', objects_for_select(
 409        AuthorPeer::doSelect(new Criteria()),
 410        'getId',
 411        '__toString()',
 412        $article->getAuthorId()
 413      )) ?>
 414      => <select name="author_id" id="author_id">
 415            <option value="1">Steve</option>
 416            <option value="2">Bob</option>
 417            <option value="3">Albert</option>
 418            <option value="4" selected="selected">Ian</option>
 419            <option value="5">Buck</option>
 420          </select>
 421  
 422  The `object_select_tag()` does all that by itself. It displays a drop-down list populated with the name of the possible records of the foreign table. The helper can guess the foreign table and foreign column from the schema, so its syntax is very concise. Listing 10-13 is equivalent to this:
 423  
 424      [php]
 425      <?php echo object_select_tag($article, 'getAuthorId') ?>
 426  
 427  The `object_select_tag()` helper guesses the related peer class name (`AuthorPeer` in the example) based on the method name passed as a parameter. However, you can specify your own class by setting the `related_class` option in the third argument. The text content of the `<option>` tags is the record name, which is the result of the `__toString()` method of the object class (if `$author->__toString()` method is undefined, the primary key is used instead). In addition, the list of options is built from a doSelect() method with an empty criteria value; it returns all the records ordered by creation date. If you prefer to display only a subset of records with a specific ordering, create a method in the peer class returning this selection as an array of objects, and set it in the `peer_method` option. Lastly, you can add a blank option or a custom option at the top of the drop-down list by setting the include_blank and include_custom options. Listing 10-14 demonstrates these different options for the `object_select_tag()` helper.
 428  
 429  Listing 10-14 - Options of the `object_select_tag()` Helper
 430  
 431      [php]
 432      // Base syntax
 433      <?php echo object_select_tag($article, 'getAuthorId') ?>
 434      // Builds the list from AuthorPeer::doSelect(new Criteria())
 435  
 436      // Change the peer class used to retrieve the possible values
 437      <?php echo object_select_tag($article, 'getAuthorId', 'related_class=Foobar') ?>
 438      // Builds the list from FoobarPeer::doSelect(new Criteria())
 439  
 440      // Change the peer method used to retrieve the possible values
 441      <?php echo object_select_tag($article, 'getAuthorId','peer_method=getMostFamousAuthors') ?>
 442      // Builds the list from AuthorPeer::getMostFamousAuthors(new Criteria())
 443  
 444      // Add an <option value="">&nbsp;</option> at the top of the list
 445      <?php echo object_select_tag($article, 'getAuthorId', 'include_blank=true') ?>
 446  
 447      // Add an <option value="">Choose an author</option> at the top of the list
 448      <?php echo object_select_tag($article, 'getAuthorId',
 449        'include_custom=Choose an author') ?>
 450  
 451  ### Updating Objects
 452  
 453  A form completely dedicated to editing object properties by using object helpers is easier to handle in an action. For instance, if you have an object of class `Author` with `name`, `age`, and `address` attributes, the form can be coded as shown in Listing 10-15.
 454  
 455  Listing 10-15 - A Form with Only Object Helpers
 456  
 457      [php]
 458      <?php echo form_tag('author/update') ?>
 459        <?php echo object_input_hidden_tag($author, 'getId') ?>
 460        Name: <?php echo object_input_tag($author, 'getName') ?><br />
 461        Age:  <?php echo object_input_tag($author, 'getAge') ?><br />
 462        Address: <br />
 463               <?php echo object_textarea_tag($author, 'getAddress') ?>
 464      </form>
 465  
 466  The `update` action of the `author` module, called when the form is submitted, can simply update the object with the `fromArray()` modifier generated by Propel, as shown in Listing 10-16.
 467  
 468  Listing 10-16 - Handling a Form Submission Based on Object Form Helpers
 469  
 470      [php]
 471      public function executeUpdate ()
 472      {
 473        $author = AuthorPeer::retrieveByPk($this->getRequestParameter('id'));
 474        $this->forward404Unless($author);
 475  
 476        $author->fromArray($this->getRequest()->getParameterHolder()->getAll(),AuthorPeer::TYPE_FIELDNAME);
 477        $author->save();
 478  
 479        return $this->redirect('/author/show?id='.$author->getId());
 480      }
 481  
 482  Form Validation
 483  ---------------
 484  
 485  Chapter 6 explained how to use the `validateXXX()` methods in the action class to validate the request parameters. However, if you use this technique to validate a form submission, you will end up rewriting the same portion of code over and over. Symfony provides an alternative form-validation technique, relying on only a YAML file, instead of PHP code in the action class.
 486  
 487  To demonstrate the form-validation features, let's first consider the sample form shown in Listing 10-17. It is a classic contact form, with `name`, `email`, `age`, and `message` fields.
 488  
 489  Listing 10-17 - Sample Contact Form, in `modules/contact/templates/indexSuccess.php`
 490  
 491      [php]
 492      <?php echo form_tag('contact/send') ?>
 493        Name:    <?php echo input_tag('name') ?><br />
 494        Email:   <?php echo input_tag('email') ?><br />
 495        Age:     <?php echo input_tag('age') ?><br />
 496        Message: <?php echo textarea_tag('message') ?><br />
 497        <?php echo submit_tag() ?>
 498      </form>
 499  
 500  The principle of form validation is that if a user enters invalid data and submits the form, the next page should show an error message. Let's define what valid data should be for the sample form, in plain English:
 501  
 502    * The `name` field is required. It must be a text entry between 2 and 100 characters.
 503    * The `email` field is required. It must be a text entry between 2 and 100 characters, and it must be a valid e-mail address.
 504    * The `age` field is required. It must be an integer between 0 and 120.
 505    * The `message` field is required.
 506  
 507  You could define more complex validation rules for the contact form, but these are just fine for a demonstration of the validation possibilities.
 508  
 509  >**NOTE**
 510  >Form validation can occur on the server side and/or on the client side. The server-side validation is compulsory to avoid corrupting a database with wrong data. The client-side validation is optional, though it greatly enhances the user experience. The client-side validation is to be done with custom JavaScript.
 511  
 512  ### Validators
 513  
 514  You can see that the `name` and `email` fields in the example share common validation rules. Some validation rules appear so often in web forms that symfony packages the PHP code that implements them into validators. A validator is simple class that provides an `execute()` method. This method expects the value of a field as parameter, and returns `true` if the value is valid and `false` otherwise.
 515  
 516  Symfony ships with several validators (described in the "Standard Symfony Validators" section later in this chapter), but let's focus on the `sfStringValidator` for now. This validator checks that an input is a string, and that its size is between two specified character amounts (defined when calling the `initialize()` method). That's exactly what is required to validate the `name` field. Listing 10-18 shows how to use this validator in a validation method.
 517  
 518  Listing 10-18 - Validating Request Parameters with Reusable Validators, in `modules/contact/action/actions.class.php`
 519  
 520      [php]
 521      public function validateSend()
 522      {
 523        $name = $this->getRequestParameter('name');
 524  
 525        // The name field is required
 526        if (!$name)
 527        {
 528          $this->getRequest()->setError('name', 'The name field cannot be left blank');
 529  
 530          return false;
 531        }
 532  
 533        // The name field must be a text entry between 2 and 100 characters
 534        $myValidator = new sfStringValidator();
 535        $myValidator->initialize($this->getContext(), array(
 536          'min'       => 2,
 537          'min_error' => 'This name is too short (2 characters minimum)',
 538          'max'       => 100,
 539          'max_error' => 'This name is too long. (100 characters maximum)',
 540        ));
 541        if (!$myValidator->execute($name))
 542        {
 543          return false;
 544        }
 545  
 546        return true;
 547      }
 548  
 549  If a user submits the form in Listing 10-17 with the value `a` in the `name` field, the `execute()` method of the `sfStringValidator` will return `false` (because the string length is less than the minimum of two characters). The `validateSend()` method will then fail, and the `handleErrorSend()` method will be called instead of the `executeSend()` method.
 550  
 551  >**TIP**
 552  >The `setError()` method of the `sfRequest` method gives information to the template so that it can display an error message (as explained in the "Displaying the Error Messages in the Form" section later in this chapter). The validators set the errors internally, so you can define different errors for the different cases of nonvalidation. That's the purpose of the `min_error` and `max_error` initialization parameters of the `sfStringValidator`.
 553  
 554  All the rules defined in the example can be translated into validators:
 555  
 556    * `name`: `sfStringValidator` (`min=2`, `max=100`)
 557    * `email`: `sfStringValidator` (`min=2`, `max=100`) and `sfEmailValidator`
 558    * `age`: `sfNumberValidator` (`min=0`, `max=120`)
 559  
 560  The fact that a field is required is not handled by a validator.
 561  
 562  ### Validation File
 563  
 564  You could easily implement the validation of the contact form with validators in the `validateSend()` method PHP, but that would imply repeating a lot of code. Symfony offers an alternative way to define validation rules for a form, and it involves YAML. For instance, Listing 10-19 shows the translation of the name field validation rules, and its results are equivalent to those of Listing 10-18.
 565  
 566  Listing 10-19 - Validation File, in `modules/contact/validate/send.yml`
 567  
 568      fields:
 569        name:
 570          required:
 571            msg:       The name field cannot be left blank
 572          sfStringValidator:
 573            min:       2
 574            min_error: This name is too short (2 characters minimum)
 575            max:       100
 576            max_error: This name is too long. (100 characters maximum)
 577  
 578  In a validation file, the `fields` header lists the fields that need to be validated, if they are required, and the validators that should be tested on them when a value is present. The parameters of each validator are the same as those you would use to initialize the validator manually. A field can be validated by as many validators as necessary.
 579  
 580  >**NOTE**
 581  >The validation process doesn't stop when a validator fails. Symfony tests all the validators and declares the validation failed if at least one of them fails. And even if some of the rules of the validation file fail, symfony will still look for a `validateXXX()` method and execute it. So the two validation techniques are complementary. The advantage is that, in a form with multiple failures, all the error messages are shown.
 582  
 583  Validation files are located in the module `validate/` directory, and named by the action they must validate. For example, Listing 10-19 must be stored in a file called `validate/send.yml`.
 584  
 585  ### Redisplaying the Form
 586  
 587  By default, symfony looks for a `handleErrorSend()` method in the action class whenever the validation process fails, or displays the `sendError.php` template if the method doesn't exist.
 588  
 589  The usual way to inform the user of a failed validation is to display the form again with an error message. To that purpose, you need to override the `handleErrorSend()` method and end it with a redirection to the action that displays the form (in the example, `module/index`), as shown in Listing 10-20.
 590  
 591  Listing 10-20 - Displaying the Form Again, in `modules/contact/actions/actions.class.php`
 592  
 593      [php]
 594      class ContactActions extends sfActions
 595      {
 596        public function executeIndex()
 597        {
 598          // Display the form
 599        }
 600  
 601        public function handleErrorSend()
 602        {
 603          $this->forward('contact', 'index');
 604        }
 605  
 606        public function executeSend()
 607        {
 608          // Handle the form submission
 609        }
 610      }
 611  
 612  If you choose to use the same action to display the form and handle the form submission, then the `handleErrorSend()` method can simply return `sfView::SUCCESS` to redisplay the form from `sendSuccess.php`, as shown in Listing 10-21.
 613  
 614  Listing 10-21 - A Single Action to Display and Handle the Form, in `modules/contact/actions/actions.class.php`
 615  
 616      [php]
 617      class ContactActions extends sfActions
 618      {
 619        public function executeSend()
 620        {
 621          if ($this->getRequest()->getMethod() != sfRequest::POST)
 622          {
 623            // Prepare data for the template
 624  
 625            // Display the form
 626            return sfView::SUCCESS;
 627          }
 628          else
 629          {
 630            // Handle the form submission
 631            ...
 632            $this->redirect('mymodule/anotheraction');
 633          }
 634        }
 635        public function handleErrorSend()
 636        {
 637          // Prepare data for the template
 638  
 639          // Display the form
 640          return sfView::SUCCESS;
 641        }
 642      }
 643  
 644  The logic necessary to prepare the data can be refactored into a protected method of the action class, to avoid repeating it in the `executeSend()` and `handleErrorSend()` methods.
 645  
 646  With this new configuration, when the user types an invalid name, the form is displayed again, but the entered data is lost and no error message explains the reason of the failure. To address the last issue, you must modify the template that displays the form, to insert error messages close to the faulty field.
 647  
 648  ### Displaying the Error Messages in the Form
 649  
 650  The error messages defined as validator parameters are added to the request when a field fails validation (just as you can add an error manually with the `setError()` method, as in Listing 10-18). The `sfRequest` object provides two useful methods to retrieve the error message: `hasError()` and `getError()`, which each expect a field name as parameter. In addition, you can display an alert at the top of the form to draw attention to the fact that one or many of the fields contain invalid data with the `hasErrors()` method. Listings 10-22 and 10-23 demonstrate how to use these methods.
 651  
 652  Listing 10-22 - Displaying Error Messages at the Top of the Form, in `templates/indexSuccess.php`
 653  
 654      [php]
 655      <?php if ($sf_request->hasErrors()): ?>
 656        <p>The data you entered seems to be incorrect.
 657        Please correct the following errors and resubmit:</p>
 658        <ul>
 659        <?php foreach($sf_request->getErrors() as $name => $error): ?>
 660          <li><?php echo $name ?>: <?php echo $error ?></li>
 661        <?php endforeach; ?>
 662        </ul>
 663      <?php endif; ?>
 664  
 665  Listing 10-23 - Displaying Error Messages Inside the Form, in `templates/indexSuccess.php`
 666  
 667      [php]
 668      <?php echo form_tag('contact/send') ?>
 669        <?php if ($sf_request->hasError('name')): ?>
 670          <?php echo $sf_request->getError('name') ?> <br />
 671        <?php endif; ?>
 672        Name:    <?php echo input_tag('name') ?><br />
 673        ...
 674        <?php echo submit_tag() ?>
 675      </form>
 676  
 677  The conditional use of the `getError()` method in Listing 10-23 is a bit long to write. That's why symfony offers a `form_error()` helper to replace it, provided that you declare the use of its helper group, `Validation`. Listing 10-24 replaces Listing 10-23 by using this helper.
 678  
 679  Listing 10-24 - Displaying Error Messages Inside the Form, the Short Way
 680  
 681      [php]
 682      <?php use_helper('Validation') ?>
 683      <?php echo form_tag('contact/send') ?>
 684  
 685                 <?php echo form_error('name') ?><br />
 686        Name:    <?php echo input_tag('name') ?><br />
 687        ...
 688        <?php echo submit_tag() ?>
 689      </form>
 690  
 691  The `form_error()` helper adds a special character before and after each error message to make the messages more visible. By default, the character is an arrow pointing down (corresponding to the `&darr;` entity), but you can change it in the `settings.yml` file:
 692  
 693      all:
 694        .settings:
 695          validation_error_prefix:    ' &darr;&nbsp;'
 696          validation_error_suffix:    ' &nbsp;&darr;'
 697  
 698  In case of failed validation, the form now displays errors correctly, but the data entered by the user is lost. You need to repopulate the form to make it really user-friendly.
 699  
 700  ### Repopulating the Form
 701  
 702  As the error handling is done through the `forward()` method (shown in Listing 10-20), the original request is still accessible, and the data entered by the user is in the request parameters. So you could repopulate the form by adding default values to each field, as shown in Listing 10-25.
 703  
 704  Listing 10-25 - Setting Default Values to Repopulate the Form When Validation Fails, in `templates/indexSuccess.php`
 705  
 706      [php]
 707      <?php use_helper('Validation') ?>
 708      <?php echo form_tag('contact/send') ?>
 709                 <?php echo form_error('name') ?><br />
 710        Name:    <?php echo input_tag('name', $sf_params->get('name')) ?><br />
 711                 <?php echo form_error('email') ?><br />
 712        Email:   <?php echo input_tag('email', $sf_params->get('email')) ?><br />
 713                 <?php echo form_error('age') ?><br />
 714        Age:     <?php echo input_tag('age', $sf_params->get('age')) ?><br />
 715                 <?php echo form_error('message') ?><br />
 716        Message: <?php echo textarea_tag('message', $sf_params->get('message')) ?><br />
 717        <?php echo submit_tag() ?>
 718      </form>
 719  
 720  But once again, this is quite tedious to write. Symfony provides an alternative way of triggering repopulation for all the fields of a form, directly in the YAML validation file, without changing the default values of the elements. Just enable the `fillin:` feature for the form, with the syntax described in Listing 10-26.
 721  
 722  Listing 10-26 - Activating `fillin` to Repopulate the Form When Validation Fails, in `validate/send.yml`
 723  
 724      fillin:
 725        enabled: true  # Enable the form repopulation
 726        param:
 727          name: test  # Form name, not needed if there is only one form in the page
 728          skip_fields:   [email]  # Do not repopulate these fields
 729          exclude_types: [hidden, password] # Do not repopulate these field types
 730          check_types:   [text, checkbox, radio, password, hidden] # Do repopulate these
 731  
 732  By default, the automatic repopulation works for text inputs, check boxes, radio buttons, text areas, and select components (simple and multiple), but it does not repopulate password or hidden tags. The `fillin` feature doesn't work for file tags.
 733  
 734  >**NOTE**
 735  >The `fillin` feature works by parsing the response content in XML just before sending it to the user. If the response is not a valid XHTML document, `fillin` might not work.
 736  
 737  You might want to transform the values entered by the user before writing them back in a form input. Escaping, URL rewriting, transformation of special characters into entities, and all the other transformations that can be called through a function can be applied to the fields of your form if you define the transformation under the `converters:` key, as shown in Listing 10-27.
 738  
 739  Listing 10-27 - Converting Input Before `fillin`, in `validate/send.yml`
 740  
 741      fillin:
 742        enabled: true
 743        param:
 744          name: test
 745          converters:         # Converters to apply
 746            htmlentities:     [first_name, comments]
 747            htmlspecialchars: [comments]
 748  
 749  ### Standard Symfony Validators
 750  
 751  Symfony contains some standard validators that can be used for your forms:
 752  
 753    * `sfStringValidator`
 754    * `sfNumberValidator`
 755    * `sfEmailValidator`
 756    * `sfUrlValidator`
 757    * `sfRegexValidator`
 758    * `sfCompareValidator`
 759    * `sfPropelUniqueValidator`
 760    * `sfFileValidator`
 761    * `sfCallbackValidator`
 762  
 763  Each has a default set of parameters and error messages, but you can easily override them through the `initialize()` validator method or in the YAML file. The following sections describe the validators and show usage examples.
 764  
 765  #### String Validator
 766  
 767  `sfStringValidator` allows you to apply string-related constraints to a parameter.
 768  
 769      sfStringValidator:
 770        values:       [foo, bar]
 771        values_error: The only accepted values are foo and bar
 772        insensitive:  false  # If true, comparison with values is case insensitive
 773        min:          2
 774        min_error:    Please enter at least 2 characters
 775        max:          100
 776        max_error:    Please enter less than 100 characters
 777  
 778  #### Number Validator
 779  
 780  `sfNumberValidator` verifies if a parameter is a number and allows you to apply size constraints.
 781  
 782      sfNumberValidator:
 783        nan_error:    Please enter an integer
 784        min:          0
 785        min_error:    The value must be more than zero
 786        max:          100
 787        max_error:    The value must be less than 100
 788  
 789  #### E-Mail Validator
 790  
 791  `sfEmailValidator` verifies if a parameter contains a value that qualifies as an e-mail address.
 792  
 793      sfEmailValidator:
 794        strict:       true
 795        email_error:  This email address is invalid
 796  
 797  RFC822 defines the format of e-mail addresses. However, it is more permissive than the generally accepted format. For instance, `me@localhost` is a valid e-mail address according to the RFC, but you probably don't want to accept it. When the `strict` parameter is set to `true` (its default value), only e-mail addresses matching the pattern `name@domain.extension` are valid. When set to `false`, RFC822 is used as a rule.
 798  
 799  #### URL Validator
 800  
 801  `sfUrlValidator` checks if a field is a correct URL.
 802  
 803      sfUrlValidator:
 804        url_error:    This URL is invalid
 805  
 806  #### Regular Expression Validator
 807  
 808  `sfRegexValidator` allows you to match a value against a Perl-compatible regular expression pattern.
 809  
 810      sfRegexValidator:
 811        match:        No
 812        match_error:  Posts containing more than one URL are considered as spam
 813        pattern:      /http.*http/si
 814  
 815  The `match` parameter determines if the request parameter must match the pattern to be valid (value `Yes`) or match the pattern to be invalid (value `No`).
 816  
 817  #### Compare Validator
 818  
 819  `sfCompareValidator` checks the equality of two different request parameters. It is very useful for password checks.
 820  
 821      fields:
 822        password1:
 823          required:
 824            msg:      Please enter a password
 825        password2:
 826          required:
 827            msg:      Please retype the password
 828          sfCompareValidator:
 829            check:    password1
 830            compare_error: The two passwords do not match
 831  
 832  The `check` parameter contains the name of the field that the current field must match to be valid.
 833  
 834  #### Propel Unique Validator
 835  
 836  `sfPropelUniqueValidator` validates that the value of a request parameter doesn't already exist in your database. It is very useful for unique indexes.
 837  
 838      fields:
 839        nickname:
 840          sfPropelUniqueValidator:
 841            class:        User
 842            column:       login
 843            unique_error: This login already exists. Please choose another one.
 844  
 845  In this example, the validator will look in the database for a record of class `User` where the `login` column has the same value as the field to validate.
 846  
 847  #### File Validator
 848  
 849  `sfFileValidator` applies format (an array of mime-types) and size constraints to file upload fields.
 850  
 851      fields:
 852        image:
 853          required:
 854            msg:      Please upload an image file
 855          file:       True
 856          sfFileValidator:
 857            mime_types:
 858              - 'image/jpeg'
 859              - 'image/png'
 860              - 'image/x-png'
 861              - 'image/pjpeg'
 862            mime_types_error: Only PNG and JPEG images are allowed
 863            max_size:         512000
 864            max_size_error:   Max size is 512Kb
 865  
 866  Be aware that the `file` attribute must be set to `True` for the field, and the template must declare the form as multipart.
 867  
 868  #### Callback Validator
 869  
 870  `sfCallbackValidator` delegates the validation to a third-party callable method or function to do the validation. The callable method or function must return `true` or `false`.
 871  
 872      fields:
 873        account_number:
 874          sfCallbackValidator:
 875            callback:      is_integer
 876            invalid_error: Please enter a number.
 877        credit_card_number:
 878          sfCallbackValidator:
 879            callback:      [myTools, validateCreditCard]
 880            invalid_error: Please enter a valid credit card number.
 881  
 882  The callback method or function receives the value to be validated as a first parameter. This is very useful when you want to reuse existing methods of functions, rather than create a full validator class.
 883  
 884  >**TIP**
 885  >You can also write your own validators, as described in the "Creating a Custom Validator" section later in this chapter.
 886  
 887  ### Named Validators
 888  
 889  If you see that you need to repeat a validator class and its settings, you can package it under a named validator. In the example of the contact form, the email field needs the same sfStringValidator parameters as the `name` field. So you can create a `myStringValidator` named validator to avoid repeating the same settings twice. To do so, add a `myStringValidator` label under the `validators:` header, and set the `class` and `param` keys with the details of the named validator you want to package. You can then use the named validator just like a regular one in the `fields` section, as shown in Listing 10-28.
 890  
 891  Listing 10-28 - Reusing Named Validators in a Validation File, in `validate/send.yml`
 892  
 893      validators:
 894        myStringValidator:
 895          class: sfStringValidator
 896          param:
 897            min:       2
 898            min_error: This field is too short (2 characters minimum)
 899            max:       100
 900            max_error: This field is too long (100 characters maximum)
 901  
 902      fields:
 903        name:
 904          required:
 905            msg:       The name field cannot be left blank
 906          myStringValidator:
 907        email:
 908          required:
 909            msg:       The email field cannot be left blank
 910          myStringValidator:
 911          sfEmailValidator:
 912            email_error:  This email address is invalid
 913  
 914  ### Restricting the Validation to a Method
 915  
 916  By default, the validators set in a validation file are run when the action is called with the POST method. You can override this setting globally or field by field by specifying another value in the `methods` key, to allow a different validation for different methods, as shown in Listing 10-29.
 917  
 918  Listing 10-29 - Defining When to Test a Field, in `validate/send.yml`
 919  
 920      methods:         [post]     # This is the default setting
 921  
 922      fields:
 923        name:
 924          required:
 925            msg:       The name field cannot be left blank
 926          myStringValidator:
 927        email:
 928          methods:     [post, get] # Overrides the global methods settings
 929          required:
 930            msg:       The email field cannot be left blank
 931          myStringValidator:
 932          sfEmailValidator:
 933            email_error:  This email address is invalid
 934  
 935  ### What Does a Validation File Look Like?
 936  
 937  So far, you have seen only bits and pieces of a validation file. When you put everything together, the validation rules find a clear translation in YAML. Listing 10-30 shows the complete validation file for the sample contact form, corresponding to all the rules defined earlier in the chapter.
 938  
 939  Listing 10-30 - Sample Complete Validation File
 940  
 941      fillin:
 942        enabled:      true
 943  
 944      validators:
 945        myStringValidator:
 946          class: sfStringValidator
 947          param:
 948            min:       2
 949            min_error: This field is too short (2 characters minimum)
 950            max:       100
 951            max_error: This field is too long (100 characters maximum)
 952  
 953      fields:
 954        name:
 955          required:
 956            msg:       The name field cannot be left blank
 957          myStringValidator:
 958        email:
 959          required:
 960            msg:       The email field cannot be left blank
 961          myStringValidator:
 962          sfEmailValidator:
 963            email_error:  This email address is invalid
 964        age:
 965          sfNumberValidator
 966            nan_error:    Please enter an integer
 967            min:          0
 968            min_error:    "You're not even born. How do you want to send a message?"
 969            max:          120
 970            max_error:    "Hey, grandma, aren't you too old to surf on the Internet?"
 971        message:
 972          required:
 973            msg:          The message field cannot be left blank
 974  
 975  Complex Validation
 976  ------------------
 977  
 978  The validation file satisfies most needs, but when the validation is very complex, it might not be sufficient. In this case, you can still return to the `validateXXX()` method in the action, or find the solution to your problem in the following sections.
 979  
 980  ### Creating a Custom Validator
 981  
 982  Each validator is a class that extends the `sfValidator` class. If the validator classes shipped with symfony are not suitable for your needs, you can easily create a new one, in any of the `lib/` directories where it can be autoloaded. The syntax is quite simple: The `execute()` method of the validator is called when the validator is executed. You can also define default settings in the `initialize()` method.
 983  
 984  The `execute()` method receives the value to validate as the first parameter and the error message to throw as the second parameter. Both are passed as references, so you can modify the error message from within the method.
 985  
 986  The `initialize()` method receives the context singleton and the array of parameters from the YAML file. It must first call the `initialize()` method of its parent `sfValidator` class, and then set the default values.
 987  
 988  Every validator has a parameter holder accessible by `$this->getParameterHolder()`.
 989  
 990  For instance, if you want to build an `sfSpamValidator` to check if a string is not spam, add the code shown in Listing 10-31 to an `sfSpamValidator.class.php` file. It checks if the `$value` contains more than `max_url` times the string `'http'`.
 991  
 992  Listing 10-31 - Creating a Custom Validator, in `lib/sfSpamValidator.class.php`
 993  
 994      [php]
 995      class sfSpamValidator extends sfValidator
 996      {
 997        public function execute (&$value, &$error)
 998        {
 999          // For max_url=2, the regexp is /http.*http/is
1000          $re = '/'.implode('.*', array_fill(0, $this->getParameter('max_url') + 1, 'http')).'/is';
1001  
1002          if (preg_match($re, $value))
1003          {
1004            $error = $this->getParameter('spam_error');
1005  
1006            return false;
1007          }
1008  
1009          return true;
1010        }
1011  
1012        public function initialize ($context, $parameters = null)
1013        {
1014          // Initialize parent
1015          parent::initialize($context);
1016  
1017          // Set default parameters value
1018          $this->setParameter('max_url', 2);
1019          $this->setParameter('spam_error', 'This is spam');
1020  
1021          // Set parameters
1022          $this->getParameterHolder()->add($parameters);
1023  
1024          return true;
1025        }
1026      }
1027  
1028  As soon as the validator is added to an autoloadable directory (and the cache cleared), you can use it in your validation files, as shown in Listing 10-32.
1029  
1030  Listing 10-32 - Using a Custom Validator, in `validate/send.yml`
1031  
1032      fields:
1033        message:
1034          required:
1035            msg:          The message field cannot be left blank
1036          sfSpamValidator:
1037            max_url:      3
1038            spam_error:   Leave this site immediately, you filthy spammer!
1039  
1040  ### Using Array Syntax for Form Fields
1041  
1042  PHP allows you to use an array syntax for the form fields. When writing your own forms, or when using the ones generated by the Propel administration (see Chapter 14), you may end up with HTML code that looks like Listing 10-33.
1043  
1044  Listing 10-33 - Form with Array Syntax
1045  
1046      [php]
1047      <label for="story[title]">Title:</label>
1048      <input type="text" name="story[title]" id="story[title]" value="default value"
1049             size="45" />
1050  
1051  Using the input name as is (with brackets) in a validation file will throw a parsed-induced error. The solution here is to replace square brackets `[]` with curly brackets `{}` in the `fields` section, as shown in Listing 10-34, and symfony will take care of the conversion of the names sent to the validators afterwards.
1052  
1053  Listing 10-34 - Validation File for a Form with Array Syntax
1054  
1055      fields:
1056        story{title}:
1057          required:     Yes
1058  
1059  ### Executing a Validator on an Empty Field
1060  
1061  You may need to execute a validator on a field that is not required, on an empty value. For instance, this happens with a form where the user can (but may not) want to change his password, and in this case, a confirmation password must be entered. See the example in Listing 10-35.
1062  
1063  Listing 10-35 - Sample Validation File for a Form with Two Password Fields
1064  
1065      fields:
1066        password1:
1067        password2:
1068          sfCompareValidator:
1069            check:         password1
1070            compare_error: The two passwords do not match
1071  
1072  The validation process executes as follows:
1073  
1074    * If `password1` `== null` and `password2 == null`:
1075  
1076      * The `required` test passes.
1077      * Validators are not run.
1078      * The form is valid.
1079  
1080    * If `password2 == null` while `password1` is not `null`:
1081  
1082      * The `required` test passes.
1083      * Validators are not run.
1084      * The form is valid.
1085  
1086  You may want to execute your `password2` validator if `password1` is `not null`. Fortunately, the symfony validators handle this case, thanks to the `group` parameter. When a field is in a group, its validator will execute if it is not empty and if one of the fields of the same group is not empty.
1087  
1088  So, if you change the configuration to that shown in Listing 10-36, the validation process behaves correctly.
1089  
1090  Listing 10-36 - Sample Validation File for a Form with Two Password Fields and a Group
1091  
1092      fields:c
1093        password1:
1094          group:           password_group
1095        password2:
1096          group:           password_group
1097          sfCompareValidator:
1098            check:         password1
1099            compare_error: The two passwords do not match
1100  
1101  The validation process now executes as follows:
1102  
1103    * If `password1 == null` and `password2 == null`:
1104  
1105      * The `required` test passes.
1106      * Validators are not run.
1107      * The form is valid.
1108  
1109    * If `password1 == null` and `password2 == 'foo'`:
1110  
1111      * The `required` test passes.
1112      * `password2` is `not null`, so its validator is executed, and it fails.
1113      * An error message is thrown for `password2`.
1114  
1115    * If `password1 == 'foo'` and `password2 == null`:
1116  
1117      * The `required` test passes.
1118      * `password1` is `not null`, so the validator for `password2`, which is in the same group, is executed, and it fails.
1119      * An error message is thrown for `password2`.
1120  
1121    * If `password1 == 'foo'` and `password2 == 'foo'`:
1122  
1123      * The `required` test passes.
1124      * `password2` is `not null`, so its validator is executed, and it passes.
1125      * The form is valid.
1126  
1127  Summary
1128  -------
1129  
1130  Writing forms in symfony templates is facilitated by the standard form helpers and their smart options. When you design a form to edit the properties of an object, the object form helpers simplify the task a great deal. The validation files, validation helpers, and repopulation feature reduce the work necessary to build a robust and user-friendly server control on the value of a field. And even the most complex validation needs can be handled, either by writing a custom validator or by creating a `validateXXX()` method in the action class.


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7