Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
Validação é uma tarefa muito comum em aplicações web. Dados inseridos em formulários precisam ser validados. Os dados também precisa ser revalidados antes de serem escritos num banco de dados ou passados a um serviço web.
O Symfony2 vem um componente Validator que torna essa tarefa fácil e transparente. Esse componente é baseado na especificação `JSR303 Bean Validation`_.
A melhor forma de entender validação é vê-la em ação. Para começar, suponha que você criou um plain-old-PHP que você precisa usar em algum lugar da sua aplicação:
1 2 3 4 5 6 7 | // src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;
class Author
{
public $name;
}
|
Até agora, essa é somente uma classe comum que serve para alguns propósitos dentro de sua aplicação. O objetivo da validação é avisar você se um dado de um objeto é válido ou não. Para esse trabalho, você irá configur uma lista de regras (chamada constraints) que o objeto deve seguir para ser válido. Essas regras podem ser especificadas por vários formatos diferentes (YAML, XML, annotations, ou PHP).
Por exemplo, para garantir que a propriedade $name
não é vazia, adicione o
seguinte:
1 2 3 4 5 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
name:
- NotBlank: ~
|
1 2 3 4 5 6 7 8 9 10 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
*/
public $name;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="Acme\BlogBundle\Entity\Author">
<property name="name">
<constraint name="NotBlank" />
</property>
</class>
</constraint-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
class Author
{
public $name;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('name', new NotBlank());
}
}
|
Tip
Propriedades protected e private podem também ser validadas, bem como os métodos
“getter” (veja validator-constraint-targets
validator
¶Próximo passo, para realmente validar um objeto``Author``, use o método validate
no serviço validator
(classe Validator
).
A tarefa do validator
é fácil: ler as restrições (i.e. regras)
de uma classe e verificar se o dado no objeto satisfaz ou não aquelas restrições.
Se a validação falhar, retorna um array de erros. Observe esse
simples exemplo de dentro do controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;
// ...
public function indexAction()
{
$author = new Author();
// ... do something to the $author object
$validator = $this->get('validator');
$errors = $validator->validate($author);
if (count($errors) > 0) {
return new Response(print_r($errors, true));
} else {
return new Response('The author is valid! Yes!');
}
}
|
Se a propriedade $name
é vazia, você verá a seguinte mensagem de
erro:
1 2 | Acme\BlogBundle\Author.name:
This value should not be blank
|
Se você inserir um valor na propriedade name
, aparecerá a feliz mensagem
de sucesso.
Tip
A maior parte do tempo, você não irá interagir diretamente com o serviço
validator
ou precisará se preocupar sobre imprimir os erros. A maior parte do tempo,
você irá usar a validação indiretamente quando lidar com dados enviados do formulário.
Para mais informações, veja: ref:book-validation-forms.
Você também poderia passar o conjunto de erros em um template.
1 2 3 4 5 6 7 | if (count($errors) > 0) {
return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
'errors' => $errors,
));
} else {
// ...
}
|
Dentro do template, você pode gerar a lista de erros exatamente necessária:
1 2 3 4 5 6 7 8 | {# src/Acme/BlogBundle/Resources/views/Author/validate.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
|
1 2 3 4 5 6 7 8 | <!-- src/Acme/BlogBundle/Resources/views/Author/validate.html.php -->
<h3>The author has the following errors</h3>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error->getMessage() ?></li>
<?php endforeach; ?>
</ul>
|
Note
Cada erro de validação (chamado de “constraint violation”), é representado por
um objeto ConstraintViolation
.
O serviço validator
pode ser usado a qualquer momento para validar qualquer objeto.
Na realidade, entretanto, você irá trabalhar frequentemente com o validator
indiretamente
enquanto trabalhar com formulário. A biblioteca Symfony’s form usa o serviço validator
internamente para validar o objeto oculto após os valores terem sido enviados
e fixados. As violações de restrição no objeto são convertidas em objetos FieldError
que podem ser facilmente exibidos com seu formulário. O tipico fluxo de envio do formulário
parece o seguinte dentro do controller:
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;
// ...
public function updateAction(Request $request)
{
$author = new Acme\BlogBundle\Entity\Author();
$form = $this->createForm(new AuthorType(), $author);
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
// the validation passed, do something with the $author object
$this->redirect($this->generateUrl('...'));
}
}
return $this->render('BlogBundle:Author:form.html.twig', array(
'form' => $form->createView(),
));
}
Note
Esse exemplo usa uma classe de formulários AuthorType
, que não é mostrada aqui.
Para mais informações, veja: doc:Forms</book/forms> chapter.
O validador do Symfony2 é abilitado por padrão, mas você deve abilitar explicitamente anotações se você usar o método de anotação para especificar suas restrições:
1 2 3 | # app/config/config.yml
framework:
validation: { enable_annotations: true }
|
1 2 3 4 | <!-- app/config/config.xml -->
<framework:config>
<framework:validation enable_annotations="true" />
</framework:config>
|
1 2 3 4 | // app/config/config.php
$container->loadFromExtension('framework', array('validation' => array(
'enable_annotations' => true,
)));
|
O validator
é designado para validar objtos perante restrições (i.e.
regras). Em ordem para validar um objeto, simplesmente mapeie uma ou mais restrições
para aquela classe e então passe para o serviço validator
.
Por trás dos bastidores, uma restrição é simplesmente um objeto PHP que faz uma sentença afirmativa. Na vida real, uma restrição poderia ser: “O bolo não deve queimar”. No Symfony2, restrições são similares: elas são afirmações que uma condição é verdadeira. Dado um valor, a restriçao irá indicar a você se o valor adere ou não às regras da restrição.
Symfony2 engloba um grande número de restrições mais frequentemente usadas:
Você também pode criar sua própria restrição personalizada. Esse tópico é coberto no artigo do cookbook “/cookbook/validation/custom_constraint” .
Algumas restrições, como NotBlank,
são simples como as outras, como a restrição Choice
, tem várias opções de configuração disponíveis. Suponha que a classe``Author``
tenha outra propriedade, gender
que possa ser configurado como
“male” ou “female”:
1 2 3 4 5 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
gender:
- Choice: { choices: [male, female], message: Choose a valid gender. }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice(
* choices = { "male", "female" },
* message = "Choose a valid gender."
* )
*/
public $gender;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="Acme\BlogBundle\Entity\Author">
<property name="gender">
<constraint name="Choice">
<option name="choices">
<value>male</value>
<value>female</value>
</option>
<option name="message">Choose a valid gender.</option>
</constraint>
</property>
</class>
</constraint-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
class Author
{
public $gender;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('gender', new Choice(array(
'choices' => array('male', 'female'),
'message' => 'Choose a valid gender.',
)));
}
}
|
A opção de uma restrição pode sempre ser passada como um array. Algumas restrições,
entretanto, também permitem a você passar o valor de uma opção “default” no lugar
do array. No cado da restrição Choice
, as opções choices
podem ser espeficadas dessa forma.
1 2 3 4 5 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
gender:
- Choice: [male, female]
|
1 2 3 4 5 6 7 8 9 10 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\Choice({"male", "female"})
*/
protected $gender;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
<class name="Acme\BlogBundle\Entity\Author">
<property name="gender">
<constraint name="Choice">
<value>male</value>
<value>female</value>
</constraint>
</property>
</class>
</constraint-mapping>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Choice;
class Author
{
protected $gender;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('gender', new Choice(array('male', 'female')));
}
}
|
Isso significa simplesmente fazer a configuração da opção mais comum de uma restrição mais curta e rápida.
Se você está incerto de como especificar uma opção, ou verifique a documentação da API para a restrição ou faça de forma segura sempre passando um array de opções (o primeiro método mostrado acima).
Restrições podem ser aplicadas a uma propriedade de classe (e.g. name
) ou
um método getter público (e.g. getFullName
). O primeiro é mais comum e fácil
de usar, mas o segundo permite você especificar regras de validação mais complexas.
Validar as propriedades de uma classe é a técnica de validação mais básica.Symfony2
permite a você validar propriedades private, protected ou public. A próxima listagem
mostra a você como configurar a propriedade $firstName
da classe Author
para ter ao menos 3 caracteres.
1 2 3 4 5 6 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
firstName:
- NotBlank: ~
- MinLength: 3
|
1 2 3 4 5 6 7 8 9 10 11 | // Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
* @Assert\MinLength(3)
*/
private $firstName;
}
|
1 2 3 4 5 6 7 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<property name="firstName">
<constraint name="NotBlank" />
<constraint name="MinLength">3</constraint>
</property>
</class>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MinLength;
class Author
{
private $firstName;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('firstName', new NotBlank());
$metadata->addPropertyConstraint('firstName', new MinLength(3));
}
}
|
Restrições podem também ser aplicadas no método de retorno de um valor.Symfony2 permite a você adicionar uma restrição para qualquer método public cujo nome comec com “get” ou “is”. Nesse guia, ambos os tipos de métodos são referidos como “getters”.
O benefício dessa técnica é que permite a você validar seu objeto
dinamicamente. Por exemplo, suponha que você queira ter certeza que um campo de senha
não coincida com o primeiro nome do usuário (por motivos de segurança). Você pode
fazer isso criando um método isPasswordLegal
, e então afirmando que
esse método deva retornar ‘’true’‘:
1 2 3 4 5 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
getters:
passwordLegal:
- "True": { message: "The password cannot match your first name" }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\True(message = "The password cannot match your first name")
*/
public function isPasswordLegal()
{
// return true or false
}
}
|
1 2 3 4 5 6 7 8 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\Author">
<getter property="passwordLegal">
<constraint name="True">
<option name="message">The password cannot match your first name</option>
</constraint>
</getter>
</class>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 | // src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\True;
class Author
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addGetterConstraint('passwordLegal', new True(array(
'message' => 'The password cannot match your first name',
)));
}
}
|
Agora, crie o método isPasswordLegal()
, e inclua a lógica que você precisa:
public function isPasswordLegal()
{
return ($this->firstName != $this->password);
}
Note
Com uma visão apurada, você irá perceber que o prefixo do getter (“get” ou “is”) é omitido no mapeamento. Isso permite você mover a restrição para uma propriedade com o mesmo nome mais tarde (ou vice-versa) sem mudar sua lógica de validação.
Algumas restrições aplicam para a classe inteira ser validada. Por exemplo, a restrição Callback é uma restrição genérica que é aplicada para a própria classe. Quando a classe é validada, métodos especificados por aquela restrição são simplesmente executadas então cada um pode prover uma validação mais personalizada.
Até agora, você foi capaz de adicionar restrições a uma classe e perguntar se aquela classe passa ou não por todas as restrições definidas. Em alguns casos, entretanto, você precisará validar um objeto a somente algumas das restrições naqula classe. Para fazer isso, você pode organizar cada restrição dentro de um ou mais “grupos de validação”, e então aplicar validação a apenas um grupo de restrições.
Por exemplo, suponha que você tenha uma classe User
, que é usada tanto quando um
usuário registra e quando um usuário atualiza sua informações de contato posteriormente:
1 2 3 4 5 6 7 8 9 10 | # src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
properties:
email:
- Email: { groups: [registration] }
password:
- NotBlank: { groups: [registration] }
- MinLength: { limit: 7, groups: [registration] }
city:
- MinLength: 2
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
class User implements UserInterface
{
/**
* @Assert\Email(groups={"registration"})
*/
private $email;
/**
* @Assert\NotBlank(groups={"registration"})
* @Assert\MinLength(limit=7, groups={"registration"})
*/
private $password;
/**
* @Assert\MinLength(2)
*/
private $city;
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<class name="Acme\BlogBundle\Entity\User">
<property name="email">
<constraint name="Email">
<option name="groups">
<value>registration</value>
</option>
</constraint>
</property>
<property name="password">
<constraint name="NotBlank">
<option name="groups">
<value>registration</value>
</option>
</constraint>
<constraint name="MinLength">
<option name="limit">7</option>
<option name="groups">
<value>registration</value>
</option>
</constraint>
</property>
<property name="city">
<constraint name="MinLength">7</constraint>
</property>
</class>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\MinLength;
class User
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('email', new Email(array(
'groups' => array('registration')
)));
$metadata->addPropertyConstraint('password', new NotBlank(array(
'groups' => array('registration')
)));
$metadata->addPropertyConstraint('password', new MinLength(array(
'limit' => 7,
'groups' => array('registration')
)));
$metadata->addPropertyConstraint('city', new MinLength(3));
}
}
|
Com essa configuração, existem dois grupos de validação:
Default
- contém as restrições não atribuidas a qualquer outro grupo;registration
- Contém as restrições somente nos campos email
e password
.Para avisar o validador a usar um grupo específico, passe um ou mais nomes de trupos como um segundo argumento para o método``validate()``
$errors = $validator->validate($author, array(‘registration’));
Claro, você irá frequentemente trabalhar com validação indiretamnte por meio da biblioteca do formulário. Para informações em como usar grupos de validação dentro de formulários, veja Grupos de Validação.
Até agora, você viu como pode validar objetos inteiros. Mas às vezes, você somente quer validar um valor simples - como verificar se uma string é um endereço de e-mail válido. Isso é realmente muito fácil de fazer. De dentro do controller, parece com isso:
// add this to the top of your class use SymfonyComponentValidatorConstraintsEmail;
public function addEmailAction($email) {
$emailConstraint = new Email(); // all constraint “options” can be set this way $emailConstraint->message = ‘Invalid email address’;
// use the validator to validate the value $errorList = $this->get(‘validator’)->validateValue($email, $emailConstraint);
- if (count($errorList) == 0) {
- // this IS a valid email address, do something
- } else {
// this is not a valid email address $errorMessage = $errorList[0]->getMessage()
// do somethign with the error
}
// ...
}
Ao chamar validateValue
no validador, você pode passar um valor bruto e
o objeto de restrição que você com o qual você quer validar aquele valor. Uma lista
completa de restrições disponíveis - bem como o nome inteiro da classe para cada
restrição - está disponível em constraints reference
section .
O método validateValule
retorna um objeto ConstraintViolationList
, que age como um array de erros. Cada erro na coleção é um objeto :
class:Symfony\Component\Validator\ConstraintViolation ,
que contém a mensagem de erro no método getMessage dele.
O Symfony2 validator
é uma ferramenta poderosa que pode ser multiplicada para
garantir que o dado de qualquer objeto seja “válido”. O poder por trás da validação
rside em “restrições”, que seão regras que você pode aplicar a propriedades ou métodos
getter de seus objetos. E enquanto você irá usar mais frequentemente usar a validação
do framework indiretamente quando usar formulários, lembre que isso pode ser usado
em qualquer lugar para validar qualquer objeto.