Un form è composto da campi, che possono essere nascosti, di testo, di selezione e di spunta. Questo capitolo introduce la creazione di form e la gestione di campi di form usando il framework per i form di symfony.
È richiesto symfony 1.1 per seguire questo libro. Si avrà anche bisogno
di creare un progetto e un'applicazione frontend
per andare avanti.
Fare riferimento all'introduzione per maggiori informazioni sulla
creazione di un progetto symfony.
Prima di iniziare
Inizieremo aggiungendo un form di contatto a un'applicazione symfony.
La Figura 1-1 mostra il form di contatto come viene visto dagli utenti che vogliono inviare un messaggio.
Figura 1-1 - Form di contatto
Creeremo tre campi per questa form: il nome dell'utente, l'email dell'utente e il messaggio che l'utente vuole inviare. Mostreremo semplicemente le informazioni inviate nel form per lo scopo di questo esercizio, come mostrato in Figura 1-2.
Figura 1-2 - Pagina di ringraziamento
Figura 1-3 - Interazione tra l'applicazione e l'utente
Widget
Le classi sfForm e sfWidget
Gli utenti inseriscono le informazioni nei campi che compongono i form.
In symfony, un form è un oggetto che eredita dalla classe sfForm
.
Nel nostro esempio, creeremo una classe ContactForm
che eredita
dalla classe sfForm
.
note
sfForm
è la classe base di tutti i form e rende facile gestire
la configurazione e il ciclo di vita dei propri form.
Si può iniziare a configurare il proprio form aggiungendo widget
usando il metodo configure()
.
Un widget rappresenta un campo di una form. Per il nostro
esempio, abbiamo bisogno di tre widget che rappresentano i nostri
tre campi: name
, email
e message
. Il Listato 1-1 mostra la
prima implementazione della classe ContactForm
.
Listato 1-1 - Classe ContactForm
con tre campi
// lib/form/ContactForm.class.php class ContactForm extends sfForm { public function configure() { $this->setWidgets(array( 'name' => new sfWidgetFormInputText(), 'email' => new sfWidgetFormInputText(), 'message' => new sfWidgetFormTextarea(), )); } }
note
In questo tutorial, non viene mai mostrata l'istruzione di apertura <?php
negli esempi che contengono solo codice PHP, in modo da ottimizzare
lo spazio e salvare un po' di alberi. Ovviamente si deve ricordare
sempre di aggiungerla, quando si crea un nuovo file PHP.
I widget sono definiti nel metodo configure()
. Questo metodo viene
chiamato automaticamente dal costruttore della classe sfForm
.
Il metodo setWidgets()
è usato dai widget nel form. Il metodo
setWidgets()
accetta un array associativo, in cui le chiavi sono
i nomi dei campi e i valori sono gli oggetti widget. Ciascun widget
è un oggetto che eredita dalla classe sfWidget
. Per questo
esempio abbiamo usato due tipi di widget:
sfWidgetFormInputText
: Questo widget rappresenta il campo di testoinput
sfWidgetFormTextarea
: Questo widget rappresenta l'area di testotextarea
note
Per convenzione, salviamo le classi dei form nella cartella lib/form/
.
Si possono salvarle in qualsiasi cartella gestita dal meccanismo
di autoloading di symfony, ma, come vedremo dopo, symfony usa la
cartella lib/form/
per generare i form dagli oggetti del modello.
Mostrare il form
Il nostro form è ora pronto per essere usato. Possiamo creare un modulo symfony per mostrare il form:
$ cd ~/PATH/TO/THE/PROJECT $ php symfony generate:module frontend contact
Nel modulo contact
, modifichiamo l'azione index per passare un'istanza
del form al template, come mostrato nel Listato 1-2.
Listato 1-2 - Classe Actions
del modulo contact
// apps/frontend/modules/contact/actions/actions.class.php class contactActions extends sfActions { public function executeIndex() { $this->form = new ContactForm(); } }
Quando si crea un form, il metodo configure()
, definito prima,
sarà chiamato automaticamente.
Ora abbiamo solo bisogno di creare un template per mostrare il form, come mostrato nel Listato 1-3.
Listato 1-3 - Template che mostra il form
// apps/frontend/modules/contact/templates/indexSuccess.php <form action="<?php echo url_for('contact/submit') ?>" method="post"> <table> <?php echo $form ?> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
Un form di symfony gestisce solo i widget che mostrano informazioni
agli utenti. Nel template indexSuccess
, la riga <?php echo $form ?>
mostra solo tre campi. Gli altri elementi come il tag form
e il
bottone submit
devono essere aggiunti dallo sviluppatore. Questo
potrebbe non sembrare ovvio inizialmente, ma vedremo più avanti quanto
sia utile e facile inserire i form.
Usare il costrutto <?php echo $form ?>
è molto utile quando si
creano e tipizzano i form. Consente allo sviluppatore di concentrarsi
sulla logica del modello senza preoccuparsi degli aspetti visuali.
Il Capitolo tre spiegherà come personalizzare il template e la
visualizzazione del form.
note
Quando si mostra un oggetto usando <?php echo $form ?>
, il motore
PHP in realtà mostrerà la rappresentazione testuale dell'oggetto
$form
. Per convertire l'oggetto in una stringa, PHP cerca di
eseguire il metodo magico __toString()
. Ciascun widget implementa
tale metodo magico per convertire l'oggetto in codice HTML.
Richiamare <?php echo $form ?>
è equivalente a chiamare
<?php echo $form->__toString() ?>
.
Possiamo ora vedere il form in un browser (Figura 1-4) e verificare
il risultato inserendo l'indirizzo dell'azione contact/index
(/frontend_dev.php/contact
).
Figura 1-4 - Form di contatto generato
Il Listato 1-4 mostra il codice generato dal template.
Listato 1-4 - Codice generato dal template
<form action="/frontend_dev.php/contact/submit" method="post"> <table> <!-- Inizio del codice generato da <?php echo $form ?> --> <tr> <th><label for="name">Nome</label></th> <td><input type="text" name="name" id="name" /></td> </tr> <tr> <th><label for="email">Email</label></th> <td><input type="text" name="email" id="email" /></td> </tr> <tr> <th><label for="message">Messaggio</label></th> <td><textarea rows="4" cols="30" name="message" id="message"></textarea></td> </tr> <!-- Fine del codice generato da <?php echo $form ?> --> <tr> <td colspan="2"> <input type="submit" /> </td> </tr> </table> </form>
Possiamo vedere che il form viene mostrato con tre righe <tr>
di
una tabella HTML. Per questo abbiamo dovuto racchiuderla in un tag
<table>
. Ogni riga comprende un tag <label>
e un tag <input>
o <textarea>
.
Label
Le etichette (label
) di ogni campo sono generate automaticamente.
Per default, le label sono una trasformazione del nome del campo seguendo
due regole: una lettera maiuscola iniziale e un trattino basso sono
sostituiti da spazi. Se il nome del campo termina per "_id", il
suffisso è rimosso dalla label.
Esempio:
$this->setWidgets(array( 'first_name' => new sfWidgetFormInputText(), // label generata: "First name" 'last_name' => new sfWidgetFormInputText(), // label generata: "Last name" 'author_id' => new sfWidgetFormInputText(), // label generata: "Author" ));
Anche se la generazione automatica delle label è molto utile, il framework
consente di definire label personalizzate col metodo setLabels()
:
$this->widgetSchema->setLabels(array( 'name' => 'Il tuo nome', 'email' => 'La tua email', 'message' => 'Il tuo messaggio', ));
Puoi anche modificare solo una singola label usando il metodo setLabel()
:
$this->widgetSchema->setLabel('email', 'La tua email');
Infine, vedremo nel Capitolo tre che si possono estendere le label dal template per personalizzare ulteriormente il form.
Oltre le tabelle generate
Anche se la visualizzazione del form per default è una tabella HTML,
il formato della visualizzazione può essere modificato. Questi diversi
tipi di formati di visualizzazione sono definiti in classi che ereditano
da sfWidgetFormSchemaFormatter
. Per default, un form usa il formato
della tabella definito nella classe sfWidgetFormSchemaFormatterTable
.
Si può anche usare il formato lista:
class ContactForm extends sfForm { public function configure() { $this->setWidgets(array( 'name' => new sfWidgetFormInputText(), 'email' => new sfWidgetFormInputText(), 'message' => new sfWidgetFormTextarea(), )); $this->widgetSchema->setFormFormatterName('list'); } }
Questi due formati sono di default e vedremo nel Capitolo 5 come creare le proprie classi per altri formati. Ora che sappiamo come visualizzare un form, vediamo come gestire l'invio.
Inviare il Form
Quando abbiamo creato un template per mostrare il form, abbiamo usato
la URL interna contact/submit
nel tag form per inviare il form.
Ora abbiamo bisogno di aggiungere l'azione submit
nel modulo contact
.
Il Listato 1-5 mostra come un'azione può ottenere l'informazione
dall'utente e redirigere alla pagina di ringraziamento dove mostriamo
questa informazione all'utente.
Listato 1-5 - Uso dell'azione submit
nel modulo contact
public function executeSubmit($request) { $this->forward404Unless($request->isMethod('post')); $params = array( 'name' => $request->getParameter('name'), 'email' => $request->getParameter('email'), 'message' => $request->getParameter('message'), ); $this->redirect('contact/thankyou?'.http_build_query($params)); } public function executeThankyou() { } // apps/frontend/modules/contact/templates/thankyouSuccess.php <ul> <li>Name: <?php echo $sf_params->get('name') ?></li> <li>Email: <?php echo $sf_params->get('email') ?></li> <li>Message: <?php echo $sf_params->get('message') ?></li> </ul>
note
http_build_query
è una funzione di libreria di PHP che genera una
query string URL-encoded da un array di parametri.
Il metodo executeSubmit()
esegue tre azioni:
Per ragioni di sicurezza, verifichiamo che la pagina sia stata inviata usando il metodo HTTP
POST
. Se non è stata inviata col metodoPOST
, l'utente è rediretto a una pagina 404. Nel templateindexSuccess
, dichiariamo il metodo di invio comePOST
(<form ... method="post">
):[php] $this->forward404Unless($request->isMethod('post'));
Poi prendiamo i valori inviati dall'utente per salvarli nella tabella dei parametri:
[php] $params = array( 'name' => $request->getParameter('name'), 'email' => $request->getParameter('email'), 'message' => $request->getParameter('message'), );
Infine, redirigiamo l'utente a una pagina di ringraziamenti (
contact/thankyou
) per mostrare le sue informazioni:[php] $this->redirect('contact/thankyou?'.http_build_query($params));
Invece di redirigere l'utente a un'altra pagina, potremmo creare un
template submitSuccess.php
. Sebbene sia possibile, è una pratica
migliore redirigere sempre l'utente dopo una richiesta con metodo POST
:
- Questo previene il reinvio del form, se l'utente ricarica la pagina
- L'utente può anche cliccare sul bottone "indietro" senza vedere l'avviso che gli chiede di inviare nuovamente il form.
tip
Forse avete notato che executeSubmit()
è diverso da executeIndex()
.
Quando chiama questi metodi, symfony passa l'oggetto sfRequest
corrente come primo parametro ai metodi executeXXX()
. Con PHP, non
occorre raccogliere tutti i parametri, perciò non abbiamo definito la
variabile di $request
in executeIndex()
, perché non ne abbiamo bisogno.
La Figura 1-5 mostra il flusso dei metodi quando interagiscono con l'utente.
Figura 1-5 - Flusso dei metodi
note
Quando si mostra di nuovo i dati immessi dall'utente nel template, si corre il rischio di un attacco XSS (Cross-Site Scripting). Si possono trovare ulteriori informazioni su come prevenire il rischio di XSS, implementando una strategia di escape nel capitolo [wiki:Documentation/it_IT/book/1.1/07-Inside-the-View-Layer All'interno del Layer Vista] della guida.
Dopo aver inviato il form, si dovrebbe vedere la pagina in Figura 1-6.
Figura 1-6 - Pagina visualizzata dopo l'invio del form
Invece di creare l'array dei parametri, sarebbe più facile ottenere le
informazioni dall'utente direttamente in un array. Il Listato 1-6
modifica l'attributo HTML name
nei widget, per memorizzare i valori
dei campi nell'array contact
.
Listato 1-6 - Modifica dell'attributo HTML name
nei widget
class ContactForm extends sfForm { public function configure() { $this->setWidgets(array( 'name' => new sfWidgetFormInputText(), 'email' => new sfWidgetFormInputText(), 'message' => new sfWidgetFormTextarea(), )); $this->widgetSchema->setNameFormat('contact[%s]'); } }
Chiamare setNameFormat()
ci consente di modificare l'attributo
HTML name
per tutti i widget. %s
verrà sostituito automaticamente
dal nome del campo durante la generazione del form. Per esempio,
l'attributo name
per il campo email
sarà contact[email]
. PHP
crea automaticamente un array con i valori di una richiesta che include
il formato contact[email]
. In questo modo i campi saranno disponibili
nell'array contact
.
Ora possiamo prendere direttamente l'array contact
dall'oggetto
richiesta, come mostrato nel Listato 1-7.
Listato 1-7 - Nuovo formato degli attributi name
nei widget dell'azione
public function executeSubmit($request) { $this->forward404Unless($request->isMethod('post')); $this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact'))); }
Quando si visualizza il sorgente HTML del form, puoi vedere che symfony
ha generato un attributo name
che dipende non solo dal nome e dal
formato del campo, ma anche dall'attributo id
. L'attributo id
viene
creato automaticamente dal nome sostituendo i caratteri non consentiti
con dei trattini bassi (_):
Nome | Attributo `name` | Attributo `id` |
---|---|---|
name | contact[name] | contact_name |
contact[email] | contact_email | |
message | contact[message] | contact_message |
Un'altra soluzione
In questo esempio, abbiamo usato due azioni per gestire il form:
index
per la visualizzazione, submit
per l'invio. Poiché il form
è visualizzato tramite il metodo GET
e l'invio tramite il metodo
POST
, possiamo anche fondere i due metodi nel metodo index
, come
mostrato nel Listato 1-8.
Listato 1-8 - Fondere le due azioni usate nel form
class contactActions extends sfActions { public function executeIndex($request) { $this->form = new ContactForm(); if ($request->isMethod('post')) { $this->redirect('contact/thankyou?'.http_build_query($request->getParameter('contact'))); } } }
Si può anche aver bisogno di cambiare l'attributo action
del form
nel template indexSuccess.php
:
<form action="<?php echo url_for('contact/index') ?>" method="post">
Come vedremo più avanti, preferiamo usare questa sintassi perché è più corta e rende il codice più coerente e comprensibile.
Configurare i Widget
Opzioni dei widget
Se un sito è gestito da diversi webmaster, ci piacerebbe certamente
aggiungere un menù a tendina con i temi, per poter redirigere il
messaggio a seconda di quanto viene richiesto (Figura 1-7). Il Listato
1-9 aggiunge un campo subject
con un menù a tendina che usa il
widget sfWidgetFormSelect
.
Figura 1-7 - Aggiungere un campo subject
al form
Listato 1-9 - Aggiungere un campo subject
al form
class ContactForm extends sfForm { protected static $subjects = array('Subject A', 'Subject B', 'Subject C'); public function configure() { $this->setWidgets(array( 'name' => new sfWidgetFormInputText(), 'email' => new sfWidgetFormInputText(), 'subject' => new sfWidgetFormSelect(array('choices' => self::$subjects)), 'message' => new sfWidgetFormTextarea(), )); $this->widgetSchema->setNameFormat('contact[%s]'); } }
Il widget sfWidgetFormSelect
, come tutti i widget, prende una
lista di opzioni come primo parametro. Un'opzione può essere
obbligatoria od opzionale. sfWidgetFormSelectwidget
ha un'opzione
obbligatoria, choiches
. Ecco le opzioni disponibili per i widget
che abbiamo già usato:
Widget | Opzioni obbligatorie | Opzioni addizionali |
---|---|---|
sfWidgetFormInput |
- | type (default a text ) |
is_hidden (default a false ) |
||
sfWidgetFormSelect |
choices |
multiple (default a false ) |
sfWidgetFormTextarea |
- | - |
tip
Se si vuol conoscere tutte le opzioni per un widget, si può fare
riferimento alla documentazione completa delle API disponibile
online su /api/1_2/. Tutte le opzioni
hanno una spiegazione, sia quelle addizionali che quelle di default.
Per esempio, tutte le opzioni per sfWidgetFormSelect
sono
disponibili qui: /api/1_2/sfWidgetFormSelect.
Gli attributi HTML dei widget
Ogni widget accetta come secondo parametro opzionale una lista
di attributi HTML. Questo è molto utile per definire degli attributi
di default per i tag del form generati. Il Listato 1-10 mostra come
aggiungere un attributo class
al campo email
.
Listato 1-10 - Definire attributi per un widget
$emailWidget = new sfWidgetFormInputText(array(), array('class' => 'email')); // HTML generato <input type="text" name="contact[email]" class="email" id="contact_email" />
Gli attributi HTML permettono anche di sovrascrivere l'identificatore generato automaticamente, come mostrato nel Listato 1-11.
Listato 1-11 - Sovrascrivere l'attributo id
$emailWidget = new sfWidgetFormInputText(array(), array('class' => 'email', 'id' => 'email')); // HTML generato <input type="text" name="contact[email]" class="email" id="email" />
È anche possibile impostare dei valori predefiniti usando l'attributo
value
, come mostra il Listato 1-12.
Listato 1-12 - Valori predefiniti per i widget usando gli attributi HTML
$emailWidget = new sfWidgetFormInputText(array(), array('value' => 'Your Email Here')); // HTML generato <input type="text" name="contact[email]" value="Your Email Here" id="contact_email" />
Questa opzione funziona per i widget input
, ma è difficile da
utilizzare con i widget checkbox
o radio
, e addirittura impossibile
con un widget textarea
. La classe sfForm
offre dei metodi specifici
per definire dei valori predefiniti per ogni campo in un modo uniforme
per tutti i tipi di widget.
note
Raccomandiamo di definire gli atttributi HTML dentro al template e non nel form stesso (anche se è possibile) per preservare i livelli di separazione, come vedremo nel Capitolo tre.
Definire dei valori predefiniti per i campi
Spesso è utile definire dei valori di default per ogni campo.
Per esempio, quando mostriamo un messaggio di aiuto nel campo, che
scompare quando l'utente fa click sul campo stesso. Il Listato 1-13
mostra come definire dei valori predefiniti usando i metodi
setDefault()
e setDefaults()
.
Listato 1-13 - Valori predefiniti dei widget usando i metodi setDefault()
e setDefaults()
class ContactForm extends sfForm { public function configure() { // ... $this->setDefault('email', 'Your Email Here'); $this->setDefaults(array('email' => 'La tua email', 'name' => 'Il tuo nome')); } }
I metodi setDefault()
e setDefaults()
sono molto utili per ogni
istanza della stessa classe di form. Se vogliamo modificare un oggetto
esistente che usa un form, i valori predefiniti dipenderanno dall'istanza,
quindi devono essere dinamici. Il Listato 1-14 mostra che il costruttore
sfForm
ha un primo parametro che imposta i valori predefiniti dinamicamente.
Listato 1-14 - Valori predefiniti dei widget usando il costruttore di sfForm
public function executeIndex($request) { $this->form = new ContactForm(array('email' => 'Your Email Here', 'name' => 'Your Name Here')); // ... }
This work is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License license.