Validazione¶
La validazione è un compito molto comune nella applicazioni web. I dati inseriti nei form hanno bisogno di essere validati. I dati hanno bisogno di essere validati anche prima di essere inseriti in una base dati o passati a un servizio web.
Symfony2 ha un componente Validator , che rende questo compito facile e trasparente. Questo componente è bastato sulle specifiche di validazione JSR303 Bean.
Le basi della validazione¶
Il modo migliore per capire la validazione è quello di vederla in azione. Per iniziare, supponiamo di aver creato un classico oggetto PHP, da usare in qualche parte della propria applicazione:
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}
Finora, questa è solo una normale classe, che ha una qualche utilità all’interno della propria applicazione. Lo scopo della validazione è dire se i dati di un oggetto siano validi o meno. Per poterlo fare, occorre configurare una lisa di regole (chiamate vincoli) che l’oggetto deve seguire per poter essere valido. Queste regole possono essere specificate tramite diversi formati (YAML, XML, annotazioni o PHP).
Per esempio, per garantire che la proprietà $name
non sia vuota, aggiungere il
seguente:
Tip
Anche le proprietà private e protette possono essere validate, così come i metodi “getter” (vedere Obiettivi dei vincoli).
Usare il servizio validator
¶
Successivamente, per validare veramente un oggetto Author
, usare il metodo
validate
sul servizio validator
(classe Symfony\Component\Validator\Validator
).
Il compito di validator
è semplice: leggere i vincoli (cioè le regole) di una
classe e verificare se i dati dell’oggetto soddisfino o no tali vincoli.
Se la validazione fallisce, viene restituita una lista di errori
(classe Symfony\Component\Validator\ConstraintViolationList
).
Prendiamo questo semplice esempio dall’interno di un controllore:
// ...
use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;
public function indexAction()
{
$autore = new Author();
// ... fare qualcosa con l'oggetto $autore
$validator = $this->get('validator');
$errori = $validator->validate($autore);
if (count($errori) > 0) {
/*
* Usa un metodo a __toString sulla variabile $errors, che è un oggetto
* ConstraintViolationList. Questo fornisce una stringa adatta
* al debug
*/
$errorsString = (string) $errori;
return new Response($errorsString);
}
return new Response('L\'autore è valido! Sì!');
}
Se la proprietà $name
è vuota, si vedrà il seguente messaggio di
errore:
Acme\BlogBundle\Author.name:
This value should not be blank
Se si inserisce un valore per la proprietà $name
, apparirà il messaggio di
successo.
Tip
La maggior parte delle volte, non si interagirà direttamente con il servizio validator
,
né ci si dovrà occupare di stampare gli errori. La maggior parte delle volte,
si userà indirettamente la validazione, durante la gestione di dati inviati tramite form. Per
maggiori informazioni, vedere Validazione e form.
Si può anche passare un insieme di errori in un template.
if (count($errors) > 0) {
return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
'errors' => $errors,
));
}
Dentro al template, si può stampare la lista di errori, come necessario:
Note
Ogni errore di validazione (chiamato “violazione di vincolo”) è rappresentato da
un oggetto Symfony\Component\Validator\ConstraintViolation
.
Validazione e form¶
Il servizio validator
può essere usato per validare qualsiasi oggetto. In realtà,
tuttavia, solitamente si lavorerà con validator
indirettamente, lavorando con i
form. La libreria dei form di Symfony usa internamente il servizio validator
, per
validare l’oggetto sottostante dopo che i valori sono stati inviati e collegati. Le
violazioni dei vincoli sull’oggetto sono convertite in oggetti FieldError
,
che possono essere facilmente mostrati con il proprio form. Il tipico flusso dell’invio
di un form assomiglia al seguente, all’interno di un controllore:
// ...
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
public function updateAction(Request $request)
{
$author = new Author();
$form = $this->createForm(new AuthorType(), $author);
$form->handleRequest($request);
if ($form->isValid()) {
// validazione passata, fare qualcosa con l'oggetto $author
return $this->redirect($this->generateUrl(...));
}
return $this->render('BlogBundle:Author:form.html.twig', array(
'form' => $form->createView(),
));
}
Note
Questo esempio usa una classe AuthorType
, non mostrata qui.
Per maggiori informazioni, vedere il capitolo sui Form.
Configurazione¶
La validazione in Symfony2 è abilitata per configurazione predefinita, ma si devono abilitare esplicitamente le annotazioni, se le si usano per specificare i vincoli:
Vincoli¶
Il servizio validator
è progettato per validare oggetti in base a vincoli (cioè
regole). Per poter validare un oggetto, basta mappare uno o più vincoli alle
rispettive classi e quindi passarli al servizio validator
.
Dietro le quinte, un vincolo è semplicemente un oggetto PHP che esegue un’istruzione assertiva. Nella vita reale, un vincolo potrebbe essere “la torta non deve essere bruciata”. In Symfony2, i vincoli sono simili: sono asserzioni sulla verità di una condizione. Dato un valore, un vincolo dirà se tale valore sia aderente o meno alle regole del vincolo.
Vincoli supportati¶
Symfony2 dispone di un gran numero dei vincoli più comunemente necessari:
Si possono anche creare i propri vincoli personalizzati. L’argomento è discusso nell’articolo “/cookbook/validation/custom_constraint” del ricettario.
Configurazione dei vincoli¶
Alcuni vincoli, come NotBlank, sono
semplici, mentre altri, come Choice, hanno
diverse opzioni di configurazione disponibili. Supponiamo che la classe Author
abbia un’altra proprietà, gender
, che possa valere solo “M” oppure
“F”:
Le opzioni di un vincolo possono sempre essere passate come array. Alcuni vincoli,
tuttavia, consentono anche di passare il valore di una sola opzione, predefinita,
al posto dell’array. Nel caso del vincolo Choice
, l’opzione choices
può
essere specificata in tal modo.
Questo ha il solo scopo di rendere la configurazione delle opzioni più comuni di un vincolo più breve e rapida.
Se non si è sicuri di come specificare un’opzione, verificare la documentazione delle API per il vincolo relativo, oppure andare sul sicuro passando sempre un array di opzioni (il primo metodo mostrato sopra).
Traduzione dei messaggi dei vincoli¶
Per informazioni sulla traduzione dei messaggi dei vincoli, vedere Tradurre i messaggi dei vincoli.
Obiettivi dei vincoli¶
I vincoli possono essere applicati alle proprietà di una classe (p.e. $name
) oppure
a un metodo getter pubblico (p.e. getFullName
). Il primo è il modo più comune e
facile, ma il secondo consente di specificare regole di validazione più complesse.
Proprietà¶
La validazione delle proprietà di una classe è la tecnica di base. Symfony2
consente di validare proprietà private, protette o pubbliche. L’elenco seguente
mostra come configurare la proprietà $firstName
di una classe Author
, per
avere almeno 3 caratteri.
Getter¶
I vincoli si possono anche applicare ai valori restituiti da un metodo. Symfony2 consente di aggiungere un vincolo a qualsiasi metodo il cui nome inizi per “get”, “is” o “has”. In questa guida, si fa riferimento a questi tipi di metodi come “getter”.
New in version 2.5: Il supporto per metodi che iniziano per has
è stato introdotto in Symfony 2.5.
Il vantaggio di questa tecnica è che consente di validare gli oggetti
dinamicamente. Per esempio, supponiamo che ci si voglia assicurare che un campo
password non corrisponda al nome dell’utente (per motivi di sicurezza). Lo si può
fare creando un metodo isPasswordLegal
e asserendo che tale metodo debba
restituire true
:
Creare ora il metodo isPasswordLegal()
e includervi la logica necessaria:
public function isPasswordLegal()
{
return $this->firstName != $this->password;
}
Note
I lettori più attenti avranno notato che il prefisso del getter (“get” o “is”) viene omesso nella mappatura. Questo consente di spostare il vincolo su una proprietà con lo stesso nome, in un secondo momento (o viceversa), senza dover cambiare la logica di validazione.
Classi¶
Alcuni vincoli si applicano all’intera classe da validare. Per esempio, il vincolo Callback è un vincolo generico, che si applica alla classe stessa. Quano tale classe viene validata, i metodi specifici di questo vincolo vengono semplicemente eseguiti, in modo che ognuno possa fornire una validazione personalizzata.
Gruppi di validazione¶
Finora, è stato possibile aggiungere vincoli a una classe e chiedere se tale classe passasse o meno tutti i vincoli definiti. In alcuni casi, tuttavia, occorre validare un oggetto solo per alcuni vincoli della sua classe. Per poterlo fare, si può organizzare ogni vincolo in uno o più “gruppi di validazione” e quindi applicare la validazione solo su un gruppo di vincoli.
Per esempio, si supponga di avere una classe User
, usata sia quando un utente
si registra che quando aggiorna successivamente le sue informazioni:
Con questa configurazione, ci sono tre gruppi di validazione:
Default
- contiene i vincoli, nella classe corrente e in tutte le classi referenziate, che non appartengono ad altri gruppi;User
- equivalente a tutti i i vincoli dell’oggettoUser
nel gruppoDefault
;registration
- contiene solo i vincoli sui campiemail
epassword
.
Per dire al validatore di usare uno specifico gruppo, passare uno o più nomi di
gruppo come secondo parametro del metodo validate()
:
// Se si usa la nuova API di validazione 2.5 (è probabile)
$errors = $validator->validate($author, null, array('registration'));
// Se si usa la vecchia API di validazione 2.4
// $errors = $validator->validate($author, array('registration'));
Se non si specifica alcun gruppo, saranno applicati tutti i vincoli che appartengono
al gruppo Default
.
Ovviamente, di solito si lavorerà con la validazione in modo indiretto, tramite la libreria dei form. Per informazioni su come usare i gruppi di validazione dentro ai form, vedere Gruppi di validatori.
Sequenza di gruppi¶
A volte si vogliono validare i gruppi in passi separati. Lo si può fare, usando
GroupSequence
. In questo caso, un oggetto definisce una sequenza di gruppi
e i gruppi in tale sequenza sono validati in ordine.
Per esempio, si supponga di avere una classe User
e di voler validare che
nome utente e password siano diversi, solo se le altre validazioni passano
(per evitare messaggi di errore multipli).
In questo esempio, prima saranno validati i vincoli del gruppo User
(che corrispondono a quelli del gruppo Default
). Solo se tutti i vincoli in
tale gruppo sono validi, sarà validato il secondo gruppo, Strict
.
Caution
Come già visto nella precedente sezione, il gruppo Default
e
il gruppo contenente il nome della classe (p.e. User
) erano identici.
Tuttavia, quando si usando le sequenza di gruppo, non lo sono più. Il gruppo
Default
farà ora riferimento alla sequenza digruppo, al posto di tutti i
vincoli che non appartengono ad alcun gruppo.
Questo vuol dire che si deve usare il gruppo {NomeClasse}
(p.e. User
)
quando si specifica una sequenza di gruppo. Quando si usa Default
, si avrà
una ricorsione infinita (poiché il gruppo Default
si riferisce alla sequenza di
gruppo, che contiene il gruppo Default
, che si riferisce alla
stessa sequenza di gruppo, ecc...).
Fornitori di sequenza di gruppo¶
Si immagini un’entità User
, che potrebbe essere un utente normale oppure premium. Se
è premium, necessita di alcuni vincoli aggiuntivi
(p.e. dettagli sulla carta di credito). Per determinare in modo dinamico quali gruppi
attivare, si può creare un Group Sequence Provider. Creare prima
l’entità e aggiungere un nuovo gruppo di vincoli, chiamato Premium
:
Cambiare ora la classe User
per implementare
Symfony\Component\Validator\GroupSequenceProviderInterface
e
aggiungere
:method:`Symfony\\Component\\Validator\\GroupSequenceProviderInterface::getGroupSequence`,
che deve restituire un array di gruppi da usare:
// src/Acme/DemoBundle/Entity/User.php
namespace Acme\DemoBundle\Entity;
// ...
use Symfony\Component\Validator\GroupSequenceProviderInterface;
class User implements GroupSequenceProviderInterface
{
// ...
public function getGroupSequence()
{
$groups = array('User');
if ($this->isPremium()) {
$groups[] = 'Premium';
}
return $groups;
}
}
Infine, occorre notificare al componente Validator che la classe User
fornisce una sequenza di gruppi da validare:
Validare valori e array¶
Finora abbiamo visto come si possono validare oggetti interi. Ma a volte si vuole validare solo un semplice valore, come verificare che una stringa sia un indirizzo email valido. Lo si può fare molto facilmente. Da dentro a un controllore, assomiglia a questo:
use Symfony\Component\Validator\Constraints\Email;
// ...
public function addEmailAction($email)
{
$emailConstraint = new Email();
// tutte le opzioni sui vincoli possono essere impostate in questo modo
$emailConstraint->message = 'Invalid email address';
// usa il validatore per validare il valore
// Se si usa la nuova API di validazione 2.5 (è probabile)
$errorList = $this->get('validator')->validate(
$email,
$emailConstraint
);
// Se si usa la vecchia API di validazione 2.4
/*
$errorList = $this->get('validator')->validateValue(
$email,
$emailConstraint
);
*/
if (count($errorList) == 0) {
// è un indirizzo email valido, fare qualcosa
} else {
// *non* è un indirizzo email valido
$errorMessage = $errorList[0]->getMessage();
// fare qualcosa con l'errore
}
// ...
}
Richiamando validateValue
sul validatore, si può passare un valore grezzo e
l’oggetto vincolo su cui si vuole validare tale valore. Una lista completa di vincoli
disponibili, così come i nomi completi delle classi per ciascun vincolo, è
disponibile nella sezione
riferimento sui vincoli.
Il metodo validateValue
restituisce un oggetto Symfony\Component\Validator\ConstraintViolationList
,
che si comporta come un array di errori. Ciascun errore della lista è un oggetto
Symfony\Component\Validator\ConstraintViolation
, che contiene
il messaggio di errore nel suo metodo getMessage
.
Considerazioni finali¶
validator
di Symfony2 è uno strumento potente, che può essere sfruttato per
garantire che i dati di qualsiasi oggetto siano validi. La potenza dietro alla
validazione risiede nei “vincoli”, che sono regole da applicare alle proprietà o
ai metodi getter del proprio oggetto. Sebbene la maggior parte delle volte si userà il
framework della validazione indirettamente, usando i form, si ricordi che può essere
usato ovunque, per validare qualsiasi oggetto.
Imparare di più con le ricette¶
- /cookbook/validation/custom_constraint