-
Notifications
You must be signed in to change notification settings - Fork 1
Validators
Validation des données :
On peut ajouter des validateurs (contraites) pour chaque objet, il y'a 02 façon de le faire soit de défnir les annotations ou un fichier yml,xml ou php.
Liste des contraites :
Contraintes de base :
Contrainte |
Rôle |
Options |
|---|---|---|
La contrainte |
- |
|
La contrainte |
- |
|
La contrainte |
|
Contraintes sur des chaînes de caractères :
Contrainte |
Rôle |
Options |
|---|---|---|
La contrainte |
|
|
La contrainte |
|
|
La contrainte |
|
|
La contrainte |
|
|
La contrainte |
|
|
La contrainte |
- |
|
La contrainte |
- |
|
La contrainte |
- |
Contraintes sur les nombres :
Contrainte |
Rôle |
Options |
|---|---|---|
La contrainte |
|
Contraintes sur les dates :
Contrainte |
Rôle |
Options |
|---|---|---|
La contrainte |
- |
|
La contrainte |
- |
|
La contrainte |
- |
Contraintes sur les fichiers :
Contrainte |
Rôle |
Options |
|---|---|---|
La contrainte |
|
|
La contrainte |
|
Le service Validator
Pour valider l'objet, on passe par un acteur externe : le service validator. Ce service s'obtient comme n'importe quel autre service :
$validator = $this->get('validator');
Ensuite, on doit demander à ce service de valider notre objet. Cela se fait grâce à la méthode validate du service. Cette méthode retourne un tableau qui est soit vide si l'objet est valide, soit rempli des différentes erreurs lorsque l'objet n'est pas valide. Pour bien comprendre, exécutez cette méthode dans un contrôleur :
public function testAction()
{
$article = new Article;
$article->setDate(new \Datetime()); // Champ « date » OK
$article->setTitre('abc'); // Champ « titre » incorrect : moins de 10 caractères
//$article->setContenu('blabla'); // Champ « contenu » incorrect : on ne le définit pas
$article->setAuteur('A'); // Champ « auteur » incorrect : moins de 2 caractères
// On récupère le service validator
$validator = $this->get('validator');
// On déclenche la validation
$liste_erreurs = $validator->validate($article);
// Si le tableau n'est pas vide, on affiche les erreurs
if(count($liste_erreurs) > 0) {
return new Response(print_r($liste_erreurs, true));
} else {
return new Response("L'article est valide !");
}
}
#####Valider depuis un getter
Mais il existe un moyen de déclencher une erreur liée à un champ en particulier, ainsi l'erreur s'affichera juste à côté de ce champ. Il suffit de nommer le getter « is + le nom d'un attribut » : par exemple isTitre si l'on veut valider le titre. Essayez par vous-mêmes le code suivant :
/**
* @Assert\True()
*/
public function isTitre()
{
return false;
}
#####Valider intelligemment un attribut objet
Derrière ce titre se cache une problématique toute simple : lorsque je valide un objet A, comment valider un objet B en attribut, d'après ses propres règles de validation ?
Il faut utiliser la contrainte Valid, qui va déclencher la validation du sous-objet B selon les règles de validation de cet objet B. Prenons un exemple :
<?php
class A
{
/**
* @Assert\MinLength(5)
*/
private $titre;
/**
* @Assert\Valid()
*/
private $b;
}
class B
{
/**
* @Assert\Min(10)
*/
private $nombre;
}
#####Valider depuis un Callback L'objectif de la contrainte Callback est d'être personnalisable à souhait. En effet, vous pouvez parfois avoir besoin de valider des données selon votre propre logique, qui ne rentre pas dans un Maxlength.
L'exemple classique est la censure de mots non désirés dans un attribut texte. Reprenons notre Article, et considérons que l'attribut contenu ne peut pas contenir les mots « échec » et « abandon ». Voici comment mettre en place une règle qui va rendre invalide le contenu s'il contient l'un de ces mots :
<?php
namespace Sdz\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Sdz\BlogBundle\Entity\Tag;
// On rajoute ce use pour le context :
use Symfony\Component\Validator\ExecutionContextInterface;
/**
* @ORM\Table()
* @ORM\Entity(repositoryClass="Sdz\BlogBundle\Entity\ArticleRepository")
*
* N'oubliez pas cet Assert :
* @Assert\Callback(methods={"contenuValide"})
*/
class Article
{
// …
public function contenuValide(ExecutionContextInterface $context)
{
$mots_interdits = array('échec', 'abandon');
// On vérifie que le contenu ne contient pas l'un des mots
if (preg_match('#'.implode('|', $mots_interdits).'#', $this->getContenu())) {
// La règle est violée, on définit l'erreur et son message
// 1er argument : on dit quel attribut l'erreur concerne, ici « contenu »
// 2e argument : le message d'erreur
$context->addViolationAt('contenu', 'Contenu invalide car il contient un mot interdit.', array(), null);
}
}
}
- L'annotation se définit ici sur la classe, et non sur une méthode ou un attribut, faites attention.
Vous auriez même pu aller plus loin en comparant des attributs entre eux, par exemple pour interdire le pseudo dans un mot de passe. L'avantage du Callback par rapport à une simple contrainte sur un getter, c'est de pouvoir ajouter plusieurs erreurs à la fois, en définissant sur quel attribut chacun se trouve grâce au premier argument de la méthode addViolationAt (en mettant contenu ou titre, etc). Souvent la contrainte sur un getter suffira, mais pensez à ce Callback pour les fois où vous serez limités.
#####Valider un champ unique
Il existe une dernière contrainte très pratique : UniqueEntity. Cette contrainte permet de valider que la valeur d'un attribut est unique parmi toutes les entités existantes. Pratique pour vérifier qu'une adresse e-mail n'existe pas déjà dans la base de données par exemple.
Vous avez bien lu, j'ai parlé d'entité. En effet, c'est une contrainte un peu particulière, car elle ne se trouve pas dans le composant Validator, mais dans le Bridge entre Doctrine et Symfony2 (ce qui fait le lien entre ces deux bibliothèques). On n'utilisera donc pas @Assert\UniqueEntity, mais simplement @UniqueEntity. Il faut bien sûr en contrepartie faire attention de rajouter ce use à chaque fois que vous l'utilisez :
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
Voici comment on pourrait, dans notre exemple avec Article, contraindre nos titres à être tous différents les uns des autres :
<?php
namespace Sdz\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
use Sdz\BlogBundle\Entity\Tag;
// On rajoute ce use pour la contrainte :
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Table()
* @ORM\Entity(repositoryClass="Sdz\BlogBundle\Entity\ArticleRepository")
*
* @UniqueEntity(fields="titre", message="Un article existe déjà avec ce titre.")
*/
class Article
{
// … Les autres contraintes ne changent pas, pas même celle(s) sur l'attribut titre
// Mais pour être logique, il faudrait aussi mettre la colonne titre en Unique pour Doctrine :
/**
* @ORM\Column(name="titre", type="string", length=255, unique=true)
*/
private $titre;
}
- Ici aussi, l'annotation se définit sur la classe, et non sur une méthode ou sur un attribut.