Development

Documentation/it_IT/book/1.1/10-Forms

You must first sign up to be able to contribute.

Capitolo 10 - Forms

CAUTION Questo capitolo descrive il modo in cui le form erano implementate in symfony 1.0. Per motivi di compatibilità e poiché l'admin generator usa questo sistema, questa informazione ha valore anche per symfony 1.1. Tuttavia, se inizi un nuovo progetto con symfony 1.1, dovresti leggere anche La guida alle form di symfony per maggiori informazioni sul nuovo framework.

Quando si scrivono i template, molto del tempo di sviluppo è dedicato alle form; nonostante questo fatto, esse risultano spesso povere di design. Dato che è necessaria molta attenzione per gestire valori di default, formattazione, ripopolamento, alcuni sviluppatori tendono a sorvolare su aspetti importanti. Per tale motivo symfony dedica speciale attenzione a questo argomento. Il presente capitolo descrive gli strumenti che automatizzano molti di tali requisiti, velocizzando lo sviluppo:

  • gli helper delle form forniscono un modo più veloce per scrivere campi di input nelle template, specialmente per elementi complessi come date, tendine, ed editor visuali.
  • Quando una form serve a modificare le proprietà di un oggetto, il templating può essere velocizzato ulteriormente tramite gli helper.
  • La validazione YAML facilita il ripopolamento e la validazione.
  • I validatori pacchettizzano il codice necessario alla validazione dell'input. Symfony include validatori per i bisogni più comuni, ed è molto semplice aggiungerne dei nuovi.

Helper delle form

Nelle template, i tag HTML degli elementi delle form sono spesso mischiati con codice PHP. Gli helper delle form in symfony hanno lo scopo di semplificare questo procedimento, evitando di aprire tag <?php echo nel mezzo di tag <input>.

Tag form principale

Come spiegato nel capitolo precedente, devi usare l'helper form_tag() per aprire una nuova form, in quanto trasforma l'azione fornita come parametro in una URL routed. Il secondo argomento può supportare opzioni aggiuntive, come ad esempio per cambiare il method di default, enctype o specificare altri attributi. Il Listato 10-1 ne mostra due esempi.

Listato 10-1 - L'helper form_tag()

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

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

Dato che non c'è alcun bisogno di un helper per chiudere una form, devi usare il classico tag </form>, anche se il codice non risulta bellissimo.

Elementi standard delle form

Con gli helper delle form, ad ogni elemento viene assegnato un id dedotto automaticamente dal proprio nome. Questa non è l'unica convenzione utile. Il Listato 10-2 mostra un elenco completo degli helper delle form e le loro opzioni.

Listato 10-2 - Sintassi degli helper delle form

[php]
// Campo di testo (input)
<?php echo input_tag('name', 'default value') ?>
 => <input type="text" name="name" id="name" value="default value" />

// Tutti gli helper delle form accettano un parametro addizionale
// che permette di generare attributi aggiuntivi
<?php echo input_tag('name', 'default value', 'maxlength=20') ?>
 => <input type="text" name="name" id="name" value="default value"maxlength="20" />

// Campo di testo lungo (text area)
<?php echo textarea_tag('name', 'default content', 'size=10x20') ?>
 => <textarea name="name" id="name" cols="10" rows="20">
      default content
    </textarea>

// Check box
<?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" />

// Radio button
<?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" />

// Tendina (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>

// Lista di opzioni per il tag select
<?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>

// Helper tendina combinato con una lista di opzioni
<?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>

// Per specificare i nomi delle opzioni, usa una array associativo
<?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>

// Tendina con selezione multipla (i valori selezionati possono essere un array)
<?php echo select_tag('payment', options_for_select(
  array('Visa' => 'Visa', 'Eurocard' => 'Eurocard', 'Mastercard' => 'Mastercard'),
  array('Visa', 'Mastecard'),
), 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>

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

// Campo password
<?php echo input_password_tag('name', 'value') ?>
 => <input type="password" name="name" id="name" value="value" />

// Campo nascosto
<?php echo input_hidden_tag('name', 'value') ?>
 => <input type="hidden" name="name" id="name" value="value" />

// Submit button (come testo)
<?php echo submit_tag('Save') ?>
 => <input type="submit" name="submit" value="Save" />

// Submit button (come immagine)
<?php echo submit_image_tag('submit_img') ?>
 => <input type="image" name="submit" src="/images/submit_img.png" />

L'helper submit_image_tag() usa la stessa sintassi ed ha gli stessi vantaggi di image_tag().

NOTE Per i radio button, l'attributo id non è impostato per default al valore del name, ma ad una combinazione del nome e del valore. Questo perchè ti servono diversi tag radio con lo stesso nome per ottenere l'effetto automatico "quando ne seleziono uno si deseleziona l'altro", e la convenzione id=name implicherebbe avere diversi tag HTML nella stessa pagina con lo stesso id, che è severamente vietato.

-

SIDEBAR Gestire il submit delle form

Come recuperi i dati inviati dagli utenti tramite form? Sono disponibili nei parametri di richiesta, per cui devi semplicemente chiamare $this->getRequestParameter($elementName) per ricevere i valori.

Una buona pratica è quella di usare la stessa azione per visualizzare e gestire la form. Secondo i metodi di richiesta (GET o POST), viene chiamata la template oppure i dati vengono gestiti e poi la richiesta viene trasferita ad un'altra azione.

[php]
// In mymodule/actions/actions.class.php
public function executeEditAuthor($request)
{
  if (!$this->getRequest()->isMethod('post'))
  {
    // Display the form
    return sfView::SUCCESS;
  }
  else
  {
    // Handle the form submission
    $name = $request->getParameter('name');
    // ...
    $this->redirect('mymodule/anotheraction');
  }
}

Per fare ciò, il target della form deve essere la stessa azione che la visualizza:

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

...

Symfony offre anche helper speciali per le form per poter fare richieste asincrone in background. Il prossimo capitolo, che affronterà Ajax, ne fornirà maggiori dettagli.

Widget per gestire le date

Le form sono spesso usate per recuperare date. Le date nel formato sbagliato sono la causa più comune per il fallimento della submit di una form. L'helper input_date_tag() assiste l'utente fornendo un utile calendario in JavaScript, se imposti l'opzione rich a true, come mostrato in Figura 10-1.

Figura 10-1 - Helper per le date (rich)

Rich date input tag

Se l'opzione rich viene omessa, l'helper stampa tre tag <select> per la selezione di giorni, mesi e anni. Puoi visualizzare questi elementi separatamente chiamando i loro helper (select_day_tag(), select_month_tag(), e select_year_tag()). I valori di default di questi elementi sono il giorno, mese e anno correnti. Il Listato 10-3 mostra gli helper delle date.

Listato 10-3 - Helper per le date (select)

[php]
<?php echo input_date_tag('dateofbirth', '2005-05-03', 'rich=true') ?>
 => a text input tag together with a calendar widget

// The following helpers require the DateForm helper group
<?php use_helper('DateForm') ?>

<?php echo select_day_tag('day', 1, 'include_custom=Choose a day') ?>
=> <select name="day" id="day">
      <option value="">Choose a day</option>
      <option value="1" selected="selected">01</option>
      <option value="2">02</option>
      ...
      <option value="31">31</option>
    </select>

<?php echo select_month_tag('month', 1, 'include_custom=Choose a month use_short_month=true') ?>
=> <select name="month" id="month">
      <option value="">Choose a month</option>
      <option value="1" selected="selected">Jan</option>
      <option value="2">Feb</option>
      ...
      <option value="12">Dec</option>
    </select>

<?php echo select_year_tag('year', 2007, 'include_custom=Choose a year year_end=2010') ?>
 => <select name="year" id="year">
      <option value="">Choose a year</option>
      <option value="2006">2006</option>
      <option value="2007" selected="selected">2007</option>
      ...
    </select>

I valori accettati per le date negli helper input_date_tag() sono quelli riconosciuti dalla funzione PHP strtotime(). Il Listato 10-4 mostra quale formato può essere usato, ed il Listato 10-5 mostra invece quelli da evitare.

Listato 10-4 - Formati accettati negli helper delle date

[php]
// Funziona bene
<?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') ?>

// Restituisce null
<?php echo input_date_tag('test', null, 'rich=true') ?>
<?php echo input_date_tag('test', '', 'rich=true') ?>

Listato 10-5 - Formati sbagliati negli helper delle date

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

// Date in formato non inglese non funzionano
<?php echo input_date_tag('test', '01/04/2006', 'rich=true') ?>

Editor Visuale

È anche possibile usare un editor visuale nei tag <textarea>, grazie all'integrazione di widget quali TinyMCE e FCKEditor. Essi forniscono un'interfaccia in stile word processor con pulsanti per formattare il testo in grassetto, corsivo ed altri stili, come mostrato in Figura 10-2.

Figura 10-2 - Rich text editing

Rich text editing

Entrambi i widget richiedono installazione manuale. Dato che la procedura è la stessa per i due widget, descriveremo qui solo quella per TinyMCE. Devi scaricare l'editor dal sito del progetto ([http://tinymce.moxiecode.com/]) e decomprimerlo in una cartella temporanea. Copia la cartella tinymce/jscripts/tiny_mce/ nella cartella web/js/ del tuo progetto, e definisci il percorso alla libreria nel file settings.yml, come mostrato nel Listato 10-6.

Listato 10-6 - Impostare la libreria dell'editor TinyMCE

all:
  .settings:
    rich_text_js_dir:  js/tiny_mce

Una volta fatto questo, imposta l'utilizzo dell'editor visuale nelle aree di testo aggiungendo l'opzione rich=true. Puoi anche specificare opzioni personalizzate per l'editor JavaScript, utilizzando l'opzione tinymce_options. Il Listato 10-7 ne mostra un esempio.

Listato 10-7 - Rich text area

[php]
<?php echo textarea_tag('name', 'default content', 'rich=true size=10x20')) ?>
 => a rich text edit zone powered by TinyMCE
<?php echo textarea_tag('name', 'default content', 'rich=true size=10x20tinymce_options=language:"fr",theme_advanced_buttons2:"separator"')) ?>
=> a rich text edit zone powered by TinyMCE with custom parameters

Selezione del paese, della lingua e della valuta

Potresti aver bisogno di visualizzare una tendina di selezione paese. Ma dato che i nomi dei paesi non sono tutti uguali nelle varie lingue, quelli presenti nell'elenco a tendina dovrebbero essere mostrati secondo la cultura dell'utente (vedi il Capitolo 13 per maggiori dettagli sulle culture). Come mostrato nel Listato 10-8, l'helper select_country_tag() fa tutto per te: internazionalizza i nomi dei paesi ed usa i codici ISO standard per i valori.

Listato 10-8 - Helper per la selezione dei paesi

[php]
<?php echo select_country_tag('country', 'AL') ?>
 => <select name="country" id="country">
      <option value="AF">Afghanistan</option>
      <option value="AL" selected="selected">Albania</option>
      <option value="DZ">Algeria</option>
      <option value="AS">American Samoa</option>
  ...

Simile all'helper select_country_tag(), select_language_tag() visualizza una lista di lingue, come mostrato nel Listato 10-9.

Listato 10-9 - Helper per le lingue

[php]
<?php echo select_language_tag('language', 'en') ?>
 => <select name="language" id="language">
      ...
      <option value="elx">Elamite</option>
      <option value="en" selected="selected">English</option>
      <option value="enm">English, Middle (1100-1500)</option>
      <option value="ang">English, Old (ca.450-1100)</option>
      <option value="myv">Erzya</option>
      <option value="eo">Esperanto</option>
      ...

Il terzo helper è select_currency_tag(), che mostra una lista di valute, come mostrato nel Listato 10-10.

Listato 10-10 - Helper per scegliere le valute

[php]
<?php echo select_currency_tag('currency', 'EUR') ?>
 => <select name="currency" id="currency">
      ...
      <option value="ETB">Ethiopian Birr</option>
      <option value="ETD">Ethiopian Dollar</option>
      <option value="EUR" selected="selected">Euro</option>
      <option value="XBA">European Composite Unit</option>
      <option value="XEU">European Currency Unit</option>
      ...

NOTE Tutti e tre gli helper accettano come terzo parametro una array di opzioni. Questa può limitare le opzioni visualizzato ad un certo sottoinsieme: array('countries' => array ('FR', 'DE')). Per select_language_tag() l'opzione si chiama languages e per select_currency_tag() si chiama currencies. Nella maggior parte dei casi ha senso limitare ad un insieme di valori conosciuti e supportati, specialmente visto che le liste potrebbero contenere dei valori desueti. select_currency_tag() offre un'ulteriore opzione chiamata display che influenza cosa viene mostrato nelle option. Può essere uno tra symbol, code o name.

Helper per gli oggetti

Quando gli elementi di una form sono usati per modificare le proprietà di un oggetto, gli helper dei link possono diventare noiosi da scrivere. Ad esempio, per modificare l'attributo telephone dell'oggetto Customer, scriveresti:

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

Per evitare di ripetere il nome dell'attributo, symfony mette a disposizione helper alternativi, per ogni oggetto della form. Un helper delle form per un oggetto deduce il nome ed il valore di default dell'elemento della form dall'oggetto e dal nome del metodo. Il precedente input_tag() è equivalente a:

[php]
<?php echo object_input_tag($customer, 'getTelephone') ?>
=> <input type="text" name="telephone" id="telephone" value="0123456789" />

Potrebbe non sembrare un grande risparmio per object_input_tag(). Comunque ogni helper standard delle form possiede il corrispettivo per l'oggetto, ed essi possiedono tutti la stessa sintassi. Ciò velocizza molto la generazione delle form. Per questo motivo gli helper degli oggetti sono usati estensivamente nello scaffolding e nella generazione dei pannelli di amministrazione (v. il Capitolo 14). Il Listato 10-11 mostra una lista di tali helper.

Listato 10-11 - Sintassi degli helper per gli oggetti

[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) ?>

Non esiste un helper object_password_tag(), in quanto è una pratica sbagliata fornire un valore di default nei tag password.

CAUTION A differenza degli helper normali delle form, quelli degli oggetti sono disponibili solo se dichiari esplicitamente l'uso dell'helper Object nella tua template, tramite use_helper('Object').

Fra tutti gli helper degli oggetti, i più interessanti sono objects_for_select() e object_select_tag(), che riguardano i menu a tendina.

Popolare menu a tendina con oggetti

L'helper options_for_select(), descritto precedentemente con gli altri helper standard, trasforma un array associativo di PHP in una lista di option, come mostrato nel Listato 10-12.

Listato 10-12 - Creare una lista di option basata su array con options_for_select()

[php]
<?php echo options_for_select(array(
  '1' => 'Steve',
  '2' => 'Bob',
  '3' => 'Albert',
  '4' => 'Ian',
  '5' => 'Buck'
), 4) ?>
 => <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>

Supponi che tu avessi già un array di oggetti della classe Author, come risultato di una query Propel. Se volessi costruire una lista di option basata su tale array, dovresti eseguire un loop al suo interno e trovare tutti gli id e name, come mostrato nel Listato 10-13.

Listato 10-13 - Creare una lista di option basata su array di oggetti con options_for_select()

[php]
// Nell'azione
$options = array();
foreach ($authors as $author)
{
  $options[$author->getId()] = $author->getName();
}
$this->options = $options;

// Nella template
<?php echo options_for_select($options, 4) ?>

Questo modo di procedere accade talmente spesso che symfony possiede un helper per automatizzarlo: objects_for_select(). L'helper si aspetta due parametri aggiuntivi: i nomi dei metodi da usare per recuperare i value ed il contenuto del testo per il tag <option>. Cosi', il Listato 10-13 è equivalente alla seguente forma più semplice:

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

Questo è facile e veloce, ma symfony fa ancora di più, quando si ha a che fare con chiavi importate.

Creare un menu a tendina basato su chiavi importate

I valori di una colonna definita come chiave importata sono le chiavi primarie di record di un'altra tabella. Se, per esempio, la tabella article ha una colonna author_id definita come chiave importata della tabella author, i valori possibili per tale colonna sono gli id dei record della tabella author. Fondamentalmente, un menu a tendina per modificare l'autore di un articolo sarebbe generato come dal Listato 10-14.

Listato 10-14 - Creare una lista di option basata su chiavi importate con objects_for_select()

[php]
<?php echo select_tag('author_id', objects_for_select(
  AuthorPeer::doSelect(new Criteria()),
  'getId',
  '__toString',
  $article->getAuthorId()
)) ?>
=> <select name="author_id" id="author_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>

L'helper object_select_tag() fa tutto da solo. Mostra un menu a tendina popolato con i nomi dei possibili record della tabella esterna. L'helper può capire la tabella e la colonna esterne dallo schema, per cui la sua sintassi è concisa. Il Listato 10-14 è equivalente a questo:

[php]
<?php echo object_select_tag($article, 'getAuthorId') ?>

L'helper object_select_tag() capisce il nome della relativa classe peer (nell'esempio AuthorPeer) dal nome del metodo passato come parametro. Ad ogni modo puoi specificare la classe che preferisci specificando l'opzione related_class come terzo argomento. Il testo che appare nel tag <option> è il nome del record, risultato del metodo __toString() dell'oggetto (se $author->__toString() non fosse definito, sarebbe riportata la chiave primaria). Inoltre, la lista delle opzioni viene costruita tramite il metodo doSelect() con un Criteria vuoto; restituisce tutti i record ordinati per data di creazione. Se preferisci visualizzare solo un sottoinsieme dei record con in ordine specifico, crea un metodo nella classe peer che restituisca tale selezione come array di oggetti, ed impostalo come opzione peer_method. Infine, puoi aggiungere una option vuota od una personalizzata all'inizio del menu a tendina impostando le opzioni include_blank e include_custom. Il Listato 10-15 mostra tali opzioni per l'helper object_select_tag().

Listato 10-15 - Opzioni per l'helper object_select_tag()

[php]
// Sintassi di base
<?php echo object_select_tag($article, 'getAuthorId') ?>
// Builds the list from AuthorPeer::doSelect(new Criteria())

// Cambiare la classe peer usata per trovare i valori possibili
<?php echo object_select_tag($article, 'getAuthorId', 'related_class=Foobar') ?>
// Builds the list from FoobarPeer::doSelect(new Criteria())

// Cambiare il metodo peer usato per trovare i valori possibili
<?php echo object_select_tag($article, 'getAuthorId','peer_method=getMostFamousAuthors') ?>
// Builds the list from AuthorPeer::getMostFamousAuthors(new Criteria())

// Aggiungere un <option value="">&nbsp;</option> all'inizio della lista
<?php echo object_select_tag($article, 'getAuthorId', 'include_blank=true') ?>

// Aggiungere un <option value="">Choose an author</option> all'inizio della lista
<?php echo object_select_tag($article, 'getAuthorId',
  'include_custom=Choose an author') ?>

Aggiornare oggetti

Una form completamente dedicata alla modifica delle proprieta' degli oggetti e che utilizza i loro helper è molto facile da gestire in un'azione. Ad esempio, se hai un oggetto della classe Author con gli attributi name, age, e address, la form può essere codificata come mostrato nel Listato 10-16.

Listato 10-16 - Una form con solamente helper degli oggetti

[php]
<?php echo form_tag('author/update') ?>
  <?php echo object_input_hidden_tag($author, 'getId') ?>
  Name: <?php echo object_input_tag($author, 'getName') ?><br />
  Age:  <?php echo object_input_tag($author, 'getAge') ?><br />
  Address: <br />
         <?php echo object_textarea_tag($author, 'getAddress') ?>
</form>

L'azione update del modulo author, chiamata quando la form viene spedita, può facilmente aggiornare l'oggetto tramite il modificatore fromArray() generato da Propel, come mostrato nel Listato 10-17.

Listato 10-17 - Gestire i dati da un form tramite gli helper degli oggetti

[php]
public function executeUpdate($request)
{
  $author = AuthorPeer::retrieveByPk($request->getParameter('id'));
  $this->forward404Unless($author);

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

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

Validazione delle form

NOTE Le caratteristiche descritte in questa sezione sono deprecate in symfony 1.1 e funzionano solamente abilitando il plugin sfCompat10.

Il Capitolo 6 ha spiegato come utilizzare i metodi validateXXX() nelle azioni delle classi per validare i parametri di richiesta. Ma se utilizzi tale tecnica per validare tutti i dati spediti da una form, finirai per scrivere ripetutamente lo stesso codice. Symfony fornisce un metodo alternativo per la validazione delle form, che si basa su un file YAML, invece di codice PHP nelle classi.

Per dimostrare tale tecnica, prendi in considerazione la semplice form del Listato 10-18. È una classica form di contatto, con i campi name, email, age, e message.

Listato 10-18 - Semplice form di contatto, 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 />
  Age:     <?php echo input_tag('age') ?><br />
  Message: <?php echo textarea_tag('message') ?><br />
  <?php echo submit_tag() ?>
</form>

Il principio della validazione delle form è che se l'utente invia dati non validi, la pagina successiva mostra un errore. Definiamo quali sono i dati validi per la form dell'esempio:

  • Il campo name è obbligatorio. Deve essere testo di lunghezza fra 2 e 100 caratteri.
  • Il campo email è obbligatorio. Deve essere testo di lunghezza fra 2 e 100 caratteri, e deve essere un indirizzo email valido.
  • Il campo age è obbligatorio. Deve essere un intero fra 0 e 120 caratteri.
  • Il campo message è obbligatorio.

Potresti definire regole molto più complesse, ma questa vanno bene per mostrare le possibilità della validazione.

NOTE La validazione delle form può avvenire lato server e/o lato client. Quella lato server è assolutamente obbligatoria per evitare la corruzione del database con dati erronei. La validazione lato client è opzionale, ma migliora molto la fruibilità. Essa è realizzata tramite JavaScript personalizzato.

Validatori

Come puoi vedere i campi name e email nell'esempio condividono una parte delle regole. Alcune regole di validazione compaiono talmente spesso nelle form che symfony pacchettizza il codice che le implementa in validatori. Un validatore è una semplice classe che dispone di un metodo execute(). Tale metodo si aspetta il valore di un campo come parametro, e restituisce true se il valore è valido, false altrimenti.

Symfony dispone di diversi validatori (descritti nella sezione "I validatori standard di symfony" più avanti in questo capitolo), ma per ora ci soffermeremo su sfStringValidator. Questo validatore accerta che l'input sia una stringa, e che la sua lunghezza sia compresa tra i due valori specifici (definiti alla chiamata del metodo initialize()). Questo è esattamente ciò che ci serve per la validazione di name. Il Listato 10-19 mostra come usare tale validatore.

Listato 10-19 - Validare parametri di richiesta tramite validatori riutilizzabili, in modules/contact/action/actions.class.php

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

  // The name field is required
  if (!$name)
  {
    $this->getRequest()->setError('name', 'The name field cannot be left blank');

    return false;
  }

  // The name field must be a text entry between 2 and 100 characters
  $myValidator = new sfStringValidator();
  $myValidator->initialize($this->getContext(), array(
    'min'       => 2,
    'min_error' => 'This name is too short (2 characters minimum)',
    'max'       => 100,
    'max_error' => 'This name is too long. (100 characters maximum)',
  ));
  if (!$myValidator->execute($name))
  {
    return false;
  }

  return true;
}

Se un utente spedisce la form del Listato 10-18 con il valore "a" nel campo name, il metodo execute() di sfStringValidator restituirà false (in quanto la lunghezza è minore di due). Quindi il metodo validateSend() fallirà, e sarà chiamato il metodo handleErrorSend() invece di executeSend().

TIP Il metodo setError() della classe sfRequest fornisce informazioni alla template affinchè essa possa mostrare il messaggio di errore (come spiegato nella sezione "Mostrare messaggi di errore nelle form" più avanti in questo capitolo). I validatori impostano gli errori internamente, quindi puoi definire errori diversi per casi diversi di dati non validi. Questo è lo scopo dei parametri di inizializzazione min_error e max_error di sfStringValidator.

Tutte le regole definite nel'esempio possono essere tradotte nei validatori:

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

Il fatto che un campo sia obbligatorio non è gestito da un validatore.

File per la validazione

Potresti facilmente implementare la validazione della form di contatto nel metodo PHP validateSend(), ma questo implicherebbe ripetere molto codice. Symfony offre un metodo alternativo per definire regole di validazione per una form, che coinvolge YAML. Ad esempio, il Listato 10-20 mostra la traduzione delle regole di validazione del campo name, ed il risultato è lo stesso di quello del Listato 10-18.

Listato 10-20 - File di validazione, in modules/contact/validate/send.yml

fields:
  name:
    required:
      msg:       The name field cannot be left blank
    sfStringValidator:
      min:       2
      min_error: This name is too short (2 characters minimum)
      max:       100
      max_error: This name is too long. (100 characters maximum)

In un file per la validazione, l'header fields elenca i campi che devono essere validati, se sono obbligatori ed i validatori da usare su di essi quando presentano dei valori. I parametri di ogni validatore sono gli stessi che useresti per inizializzare un validatore manualmente. Un campo può essere validato da tanti validatori quanti ne siano necessari.

NOTE Il processo di validazione non si ferma quando un validatore fallisce. Symfony controlla tutti i validatori e dichiara il fallimento se almeno uno di essi fallisce. Ed anche se alcune delle regole falliscono, symfony continuerà a cercare un metodo validateXXX() e ad eseguirlo. Per cui le due tecniche di validazione sono complementari. Il vantaggio è che in una form con fallimenti multipli, tutti gli errori vengono mostrati.

I file per la validazione sono situati nella cartella validate/ di ogni modulo, e portano il nome dell'azione che devono validare. Ad esempio, il Listato 10-20 deve essere memorizzato in un file chiamato validate/send.yml.

Rivisualizzare la form

Per default, ogni qualvolta un processo di validazione fallisca, symfony controlla l'esistenza del metodo handleErrorSend() nell'azione, e se non esiste mostra la template sendError.php.

Il metodo abituale di informare l'utente di una validazione fallita è quello di mostrare nuovamente la form con un messaggio di errore. A questo scopo, devi fare l'override del metodo handleErrorSend() e concluderlo con la ridirezione all'azione che mostra la form (nell'esempio, module/index), come mostrato nel Listato 10-21.

Listato 10-21 - Mostrare nuovamente la form, in modules/contact/actions/actions.class.php

[php]
class ContactActions extends sfActions
{
  public function executeIndex()
  {
    // Display the form
  }

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

  public function executeSend()
  {
    // Handle the form submission
  }
}

Se scegli di utilizzare la stessa azione sia per visualizzare la form che per gestirne i dati, allora il metodo handleErrorSend() può semplicemente restituire sfView::SUCCESS per rivisualizzare la form tramite sendSuccess.php, come mostrato nel Listato 10-22.

Listato 10-22 - Un'unica azione per visualizzare e gestire la form, in modules/contact/actions/actions.class.php

[php]
class ContactActions extends sfActions
{
  public function executeSend()
  {
    if (!$this->getRequest()->isMethod('post'))
    {
      // Prepara i dati per la template

      // Visualizza la form
      return sfView::SUCCESS;
    }
    else
    {
      // Gestisce i dati
      ...
      $this->redirect('mymodule/anotheraction');
    }
  }
  public function handleErrorSend()
  {
    // Prepara i dati per la template

    // Visualizza la form
    return sfView::SUCCESS;
  }
}

La logica necessaria alla preparazione dei dati per la template può essere spostata (refactoring) in un metodo protetto, così da evitare la duplicazione del codice nei metodi executeSend() e handleErrorSend().

Con questa nuova configurazione, quando un utente scrive un nome non valido la form viene visualizzata nuovamente, ma i dati che aveva immesso sono perduti e non c'è alcun messaggio che spiega la natura dell'errore. A questo scopo devi modificare la template che visualizza la form, per inserire un messaggio di errore vicino al campo che lo ha generato.

Mostrare messaggi di errore nelle form

I messaggi di errore definiti come parametri dei validatori vengono aggiunti alla richiesta quando un campo fallisce la validazione (esattamente come se tu aggiungessi un errore manualmente con setError(), come mostrato nel Listato 10-19). L'oggetto sfRequest fornisce due metodi utili per recuperare il messaggio di errore: hasError() e getError(), ognuno dei quali si aspetta un nome di campo come parametro. Inoltre puoi visualizzare un messaggio all'inizio della form tramite il metodo hasErrors() per far notare il fatto che ci sono uno o più campi che hanno fallito la validazione. I Listati 10-23 e 10-24 mostrano come usare tali metodi.

Listato 10-22 - Mostrare un messaggio di errore all'inizio della form, in templates/indexSuccess.php

[php]
<?php if ($sf_request->hasErrors()): ?>
  <p>The data you entered seems to be incorrect.
  Please correct the following errors and resubmit:</p>
  <ul>
  <?php foreach($sf_request->getErrors() as $name => $error): ?>
    <li><?php echo $name ?>: <?php echo $error ?></li>
  <?php endforeach; ?>
  </ul>
<?php endif; ?>

Listato 10-23 - Mostrare un messaggio di errore all'interno della form, 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>

L'uso condizionale del metodo getError() del Listato 10-23 è un pò lungo da scrivere. Per tale motivo symfony mette a disposizione un helper form_error(), a patto che tu abbia definito l'utilizzo del gruppo di helper di cui fa parte, Validation. Il Listato 10-25 sostituisce il 10-24 con l'utilizzo di tale helper.

Listato 10-25 - Mostrare un messaggio di errore all'interno della form, metodo più breve

[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>

L'helper form_error() aggiunge un carattere speciale prima e dopo il messaggio di errore, per renderlo più visibile. Per default, tale carattere è una freccia rivolta verso il basso (corrispondente all'entità ↓), ma puoi cambiarlo nel file settings.yml:

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

In caso di validazione fallita, ora la form mostra correttamente gli errori, ma i dati inseriti dall'utente sono perduti. Per rendere la form più user-friendly occorre ripopolarla.

Ripopolazione della form

Siccome la gestione dell'errore avviene tramite il metodo forward() (come mostrato nel Listato 10-21), la richiesta originale è ancora accessibile, ed i dati inseriti dall'utente sono nei parametri di richiesta. Quindi potresti ripopolare la form aggiungendo valori di default, come mostrato nel Listato 10-26.

Listato 10-26 - Impostare valori di default per ripopolare la form quando la validazione fallisce, 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('age') ?><br />
  Age:     <?php echo input_tag('age', $sf_params->get('age')) ?><br />
           <?php echo form_error('message') ?><br />
  Message: <?php echo textarea_tag('message', $sf_params->get('message')) ?><br />
  <?php echo submit_tag() ?>
</form>

Ma ancora una volta, tale metodo è noioso da scrivere. Symfony fornisce un metodo alternativo per la ripopolazione, basato su un file YAML, senza modificare i valori di default degli elementi. Abilita semplicemente la funzione fillin: per la form, con la sintassi descritta nel Listato 10-27.

Listato 10-27 - Attivare fillin per ripopolare la form, in validate/send.yml

fillin:
  enabled: true  # Enable the form repopulation
  param:
    name: test  # Form name, not needed if there is only one form in the page
    skip_fields:   [email]  # Do not repopulate these fields
    exclude_types: [hidden, password] # Do not repopulate these field types
    check_types:   [text, checkbox, radio, password, hidden] # Do repopulate these

Per default, la ripopolazione automatica funziona per gli elementi text input, check boxe, radio button, text area, e select (normali e multiple), ma non per le password ed i tag hidden. La funzione fillin non funziona per i tag file.

NOTE La funzione fillin funziona facendo il parsing in XML del contenuto della risposta immediatamente prima di spedirla all'utente. Se la risposta non è un documento XHTML valido, fillin potrebbe non funzionare correttamente.

Potresti voler trasformare i valori inseriti dall'utente prima di riscriverli nella form. Escaping, URL rewriting, trasformazione di caratteri speciali in entità, e tutte le altre trasformazioni che possono essere chiamate tramite una funzione possono essere applicate ai campi della tua form se definisci tale trasformazione sotto la chiave converters:, come mostrato nel Listato 10-28.

Listato 10-28 - Convertire input prima del fillin, in validate/send.yml

fillin:
  enabled: true
  param:
    name: test
    converters:         # Converters to apply
      htmlentities:     [first_name, comments]
      htmlspecialchars: [comments]

Validatori standard di symfony

Symfony include alcuni validatori standard che puoi utilizzare nelle tue form:

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

Ognuno di essi possiede un insieme di parametri di default e messaggi di errore, ma ne puoi fare facilmente l'override tramite il metodo initialize() dei validatori, od in un file YAML. La sezione seguente descrive i validatori e ne mostra esempi d'uso.

Validatore di stringhe

sfStringValidator permette di applicare restrizioni di tipo stringa ad un parametro.

sfStringValidator:
  values:       [foo, bar]
  values_error: The only accepted values are foo and bar
  insensitive:  false  # If true, comparison with values is case insensitive
  min:          2
  min_error:    Please enter at least 2 characters
  max:          100
  max_error:    Please enter less than 100 characters

Validatore di numeri

sfNumberValidator controlla se un parametro è un numero e permette di applicare restrizioni sulla dimensione.

sfNumberValidator:
  nan_error:    Please enter an integer
  min:          0
  min_error:    The value must be at least zero
  max:          100
  max_error:    The value must be less than or equal to 100

Validatore di email

sfEmailValidator controlla se un parametro è un indirizzo di email valido.

sfEmailValidator:
  strict:       true
  email_error:  This email address is invalid

RFC822 definisce il formato per gli indirizzi email. Comunque esso è più permissivo dei formati generalmente accettati. Ad esempio, secondo l'RFC me@localhost è un indirizzo di email valido, ma probabilmente tu non lo vorresti accettare. Quando il parametro strict è impostato a true (che è il suo valore di default), solo le email che corrispondono al pattern name@domain.extension sono valide. Quando è impostato su false, come regola viene usata RFC822.

Validatore di URL

sfUrlValidator controlla se un campo è una URL corretta.

sfUrlValidator:
  url_error:    This URL is invalid

Validatore di espressioni regolari

sfRegexValidator ti permette di confrontare un valore con un una espressione regolare in formato Perl.

sfRegexValidator:
  match:        No
  match_error:  Posts containing more than one URL are considered as spam
  pattern:      /http.*http/si

Il parametro match determina se per essere valido un parametro di richiesta deve corrispondere ad pattern (valore Yes) oppure no (valore No).

Validatore di confronto

sfCompareValidator confronta due parametri di richiesta. Risulta molto utile per il controllo delle password.

fields:
  password1:
    required:
      msg:      Please enter a password
  password2:
    required:
      msg:      Please retype the password
    sfCompareValidator:
      check:    password1
      compare_error: The two passwords do not match

Il parametro check contiene il nome del campo che deve essere coincidente al campo in esame.

Per default il validatore controlla che i parametri siano uguali. Puoi cambiare questo comportamento specificando un parametro operator. Gli operatori possibili sono: >, >=, <, <=, == e !=.

Validatore di Propel unici

sfPropelUniqueValidator assicura che il valore di un parametro di richiesta non esista già nel tuo database. Risulta molto utile per indici unici.

fields:
  nickname:
    sfPropelUniqueValidator:
      class:        User
      column:       login
      unique_error: This login already exists. Please choose another one.

In questo esempio, il validatore controlla se nel database esiste già un record della classe User dove la colonna column abbia lo stesso valore del campo da verificare.

Validatore di file

sfPropelFileValidator applica restrizioni sul formato (array di mime-type) e sulle dimensioni dei campi di upload.

fields:
  image:
    required:
      msg:      Please upload an image file
      file:     True
    sfFileValidator:
      mime_types:
        - 'image/jpeg'
        - 'image/png'
        - 'image/x-png'
        - 'image/pjpeg'
      mime_types_error: Only PNG and JPEG images are allowed
      max_size:         512000
      max_size_error:   Max size is 512Kb

Fai attenzione a che l'attributo file sia impostato a True, e che la template dichiari la form come multipart.

Validatore callback

sfPropelCallbackValidator delega la validazione ad un metodo di terze parti. Tale metodo deve restituire true o false.

fields:
  account_number:
    sfCallbackValidator:
      callback:      is_integer
      invalid_error: Please enter a number.
  credit_card_number:
    sfCallbackValidator:
      callback:      [myTools, validateCreditCard]
      invalid_error: Please enter a valid credit card number.

Il metodo o la funzione di callback riceve il valore da verificare come primo parametro. Risulta molto utile quando vuoi riutilizzare metodi o funzioni esistenti, invece di scrivere un'intera classe per la validazione.

TIP Puoi anche scrivere i tuoi validatori, come descritto nella sezione "Creare un validatore personalizzato" più avanti in questo capitolo.

Chiamate a validatori

Se ti rendi conto che hai bisogno di ripetere un validatore e le sue impostazioni, lo puoi pacchettizzare in una chiamata a validatore. Nell'esempio della form di contatto, il campo email necessita dello stesso sfStringValidator del campo name. Per cui potresti creare la tua chiamata myStringValidator per evitare di ripetere le stesse impostazioni. Per fare ciò, aggiungi una label myStringValidator sotto l'header validators:, ed imposta le chiavi class e param con i dettagli relativi. Puoi utilizzare la chiamata sotto la sezione fields come un qualsiasi validatore regolare, come mostrato nel Listato 10-29.

Listato 10-29 - Riutilizzare chiamate a validatori, in validate/send.yml

validators:
  myStringValidator:
    class: sfStringValidator
    param:
      min:       2
      min_error: This field is too short (2 characters minimum)
      max:       100
      max_error: This field is too long (100 characters maximum)

fields:
  name:
    required:
      msg:       The name field cannot be left blank
    myStringValidator:
  email:
    required:
      msg:       The email field cannot be left blank
    myStringValidator:
    sfEmailValidator:
      email_error:  This email address is invalid

Restringere la validazione ad un metodo

Per default, i validatori impostati vengono utilizzati ogni qualvolta un'azione riceve parametri in POST. Puoi fare l'override di tale impostazione sia globalmente che campo per campo, specificando un altro valore nella chiave method, per permettere validazioni differenti su metodi differenti, come mostrato nel Listato 10-30.

Listato 10-30 - Definire quando testare un campo, in validate/send.yml

methods:         [post]     # This is the default setting

fields:
  name:
    required:
      msg:       The name field cannot be left blank
    myStringValidator:
  email:
    methods:     [post, get] # Overrides the global methods settings
    required:
      msg:       The email field cannot be left blank
    myStringValidator:
    sfEmailValidator:
      email_error:  This email address is invalid

Com'è fatto un file di validazione?

Fino ad ora hai visto solo pezzetti di file di validazione. Quando assembli il tutto, le regole di validazione trovano una chiara traduzione in YAML. Il Listato 10-31 mostra un file completo per la validazione dell'esempio della form di contatto, contenente tutte le regole viste finora.

Listato 10-31 - File di validazione completo

fillin:
  enabled:      true

validators:
  myStringValidator:
    class: sfStringValidator
    param:
      min:       2
      min_error: This field is too short (2 characters minimum)
      max:       100
      max_error: This field is too long (100 characters maximum)

fields:
  name:
    required:
      msg:       The name field cannot be left blank
    myStringValidator:
  email:
    required:
      msg:       The email field cannot be left blank
    myStringValidator:
    sfEmailValidator:
      email_error:  This email address is invalid
  age:
    sfNumberValidator
      nan_error:    Please enter an integer
      min:          0
      min_error:    "You're not even born. How do you want to send a message?"
      max:          120
      max_error:    "Hey, grandma, aren't you too old to surf on the Internet?"
  message:
    required:
      msg:          The message field cannot be left blank

Validazione complessa

Il file per la validazione soddisfa certamente la maggior parte dei bisogni, ma all'aumentare della complessita' potrebbe non bastare piu'. In tale caso, puoi restituire il metodo validateXXX() nell'azione, o trovare la soluzione al tuo problema nella sezione seguente.

Creare un validatore personalizzato

Ogni validatore non è altro che una classa che estende sfValidator. Se quelli inclusi in symfony non soddisfano i tuoi bisogni, ne puoi creare uno personalizzato e posizionarlo in una delle cartelle lib/ in modo che venga autocaricato. La sintassi è abbastanza semplice: viene chiamato il metodo execute() all'esecuzione del validatore. Puoi anche definire impostazioni dei default nel metodo initialize().

Il metodo execute() riceve come primo parametro il valore da validare, e l'errore da restituire come secondo. Entrambi vengono passati per riferimento, quindi puoi modificare il messaggio all'interno del metodo.

Il metodo initialize() riceve il singleton ed un array di parametri dal file YAML. Esso deve prima di tutto chiamare il metodo initialize() del parent (sfValidator), quindi impostare i valori di default.

Ogni validatore possiede un parameter holder accessibile tramite $this->getParameterHolder().

Ad esempio, se volessi creare un validatore sfSpamValidator per controllare che una stringa non contenga spam, inserisci il codice del Listato 10-31 in un file sfSpamValidator.class.php. Esso controlla che $value non contenga più di max_url volte la stringa 'http'.

Listato 10-31 - Creare un validatore personalizzato, in lib/sfSpamValidator.class.php

[php]
class sfSpamValidator extends sfValidator
{
  public function execute(&$value, &$error)
  {
    // For max_url=2, the regexp is /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)
  {
    // Initialize parent
    parent::initialize($context);

    // Set default parameters value
    $this->setParameter('max_url', 2);
    $this->setParameter('spam_error', 'This is spam');

    // Set parameters
    $this->getParameterHolder()->add($parameters);

    return true;
  }
}

Appena il file è posizionato in una cartella su cui funziona l'autocaricamento (e la cache svuotata) puoi utilizzare il validatore, come mostrato nel Listato 10-33.

Listato 10-33 - Utilizzare un validatore personalizzato, in validate/send.yml

fields:
  message:
    required:
      msg:          The message field cannot be left blank
    sfSpamValidator:
      max_url:      3
      spam_error:   Leave this site immediately, you filthy spammer!

Utilizzare la sintassi con array per le form

PHP permette di usare una sintassi con array nelle form. Sia che tu scriva le tue form, sia che tu usi quelle generate dall'amministrazione di Propel (v. Capitolo 14), puoi avere del codice come quello del Listato 10-34.

Listato 10-34 - Form con sintassi di array

[php]
<label for="story[title]">Title:</label>
<input type="text" name="story[title]" id="story[title]" value="default value"
       size="45" />

Utilizzare tale nome del campo input (con le parentesi quadre) in un file per la validazione generera' un errore di parsing. La soluzione in questo caso è quella di usare le parentesi graffe invece delle quadre nella sezione fields, come mostrato nel Listato 10-35, e symfony si occupera' del resto.

Listato 10-35 - File per la validazione con sintassi di array

fields:
  story{title}:
    required:     Yes

Eseguire un validatore su un campo vuoto

Potresti aver bisogno di eseguire una validazione su un campo non obbligatorio, od un valore vuoto. Ad esempio, ciò potrebbe succedere in una form dove un utente può (ma non è obbligato) cambiare la propria password, e in tale caso la deve confermare.

Listato 10-36 - Esempio di file per la validazione per una form con due campi password

fields:
  password1:
  password2:
    sfCompareValidator:
      check:         password1
      compare_error: The two passwords do not match

Il processo di validazione viene eseguito così:

  • Se password1 == null e password2 == null:

    • Il test required è ok
    • I validatori non vengono eseguiti
    • La form è valida
  • Se password2 == null mentre password1 no:

    • Il test required è ok
    • I validatori non vengono eseguiti
    • La form è valida

Potresti voler eseguire il validatore su password2 se password1 non è vuota. Fortunatamente i validatori di symfony gestiscono tale eventualita', grazie al parametro group. Quando un campo fa parte di un gruppo, il proprio validatore verrà eseguito se non è vuoto o se uno dei campi dello stesso gruppo non è vuoto.

Quindi, se cambi la tua configurazione come quella mostrata nel Listato 10-37, il processo di validazione si comporterà correttamente.

Listato 10-37 - Esempio di file per la validazione per una form con due campi password ed un gruppo

fields:c
  password1:
    group:           password_group
  password2:
    group:           password_group
    sfCompareValidator:
      check:         password1
      compare_error: The two passwords do not match

Ora il processo di validazione si comporta cosi':

  • Se password1 == null e password2 == null:

    • Il test required è ok
    • I validatori non vengono eseguiti
    • La form è valida
  • Se password1 == null e password2 == 'foo':

    • Il test required è ok
    • password2 non è vuota, per cui il suo validatore viene eseguito e fallisce
    • Viene mostrato un messaggio di errore per password2
  • Se password1 == 'foo' e password2 == null:

    • Il test required è ok
    • password1 non è vuota, per cui il validatore per password2, che fa parte dello stesso gruppo, viene eseguito e fallisce
    • Viene mostrato un messaggio di errore per password2
  • Se password1 == 'foo' e password2 == 'foo':

    • Il test required è ok
    • password2 non è vuota, per cui il suo validatore viene eseguito ed ha successo
    • La form è valida

Sommario

Scrivere form nelle template di symfony è un compito facilitato dagli helper e dalle loro opzioni. Quando disegni una form per modificare le proprieta' di un oggetto, gli helper degli oggetti semplificano notevolmente il tuo lavoro. I file per la validazione, gli helper per la validazione e la funzionalita' di ripopolamento delle form riducono notevolmente il tempo necessario alla costruzione di un robusto e user-friendly controllo sui valori dei campi. Ed anche nel caso in cui ti serva una validazione molto complessa, puoi scrivere il tuo validatore personalizzato o creare un metodo validateXXX() nell'azione.