Validation

Validation is a very common task in web applications. Data entered in formsneeds to be validated. Data also needs to be validated before it is writteninto a database or passed to a web service.

Symfony provides a Validator component that makes this task easy andtransparent. This component is based on the JSR303 Bean Validation specification.

Installation

In applications using Symfony Flex, run this command toinstall the validator before using it:

  1. $ composer require symfony/validator doctrine/annotations

The Basics of Validation

The best way to understand validation is to see it in action. To start, supposeyou've created a plain-old-PHP object that you need to use somewhere inyour application:

  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. class Author
  5. {
  6. private $name;
  7. }

So far, this is just an ordinary class that serves some purpose inside yourapplication. The goal of validation is to tell you if the dataof an object is valid. For this to work, you'll configure a list of rules(called constraints) that the object mustfollow in order to be valid. These rules are usually defined using PHP code orannotations but they can also be defined as .yaml or.xml files inside the config/validator/ directory:

For example, to guarantee that the $name property is not empty, add thefollowing:

  • Annotations
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6.  
  7. class Author
  8. {
  9. /**
  10. * @Assert\NotBlank
  11. */
  12. private $name;
  13. }
  • YAML
  1. # config/validator/validation.yaml
  2. App\Entity\Author:
  3. properties:
  4. name:
  5. - NotBlank: ~
  • XML
  1. <!-- config/validator/validation.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
  6. https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
  7.  
  8. <class name="App\Entity\Author">
  9. <property name="name">
  10. <constraint name="NotBlank"/>
  11. </property>
  12. </class>
  13. </constraint-mapping>
  • PHP
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3. // ...
  4. use Symfony\Component\Validator\Constraints\NotBlank;
  5. use Symfony\Component\Validator\Mapping\ClassMetadata;
  6.  
  7. class Author
  8. {
  9. private $name;
  10.  
  11. public static function loadValidatorMetadata(ClassMetadata $metadata)
  12. {
  13. $metadata->addPropertyConstraint('name', new NotBlank());
  14. }
  15. }

Tip

Symfony's validator uses PHP reflection, as well as "getter" methods, toget the value of any property, so they can be public, private or protected(see Constraint Targets).

Using the validator Service

Next, to actually validate an Author object, use the validate() methodon the validator service (which implements ValidatorInterface).The job of the validator is to read the constraints (i.e. rules)of a class and verify if the data on the object satisfies thoseconstraints. If validation fails, a non-empty list of errors(class ConstraintViolationList) isreturned. Take this simple example from inside a controller:

  1. // ...
  2. use App\Entity\Author;
  3. use Symfony\Component\HttpFoundation\Response;
  4. use Symfony\Component\Validator\Validator\ValidatorInterface;
  5.  
  6. // ...
  7. public function author(ValidatorInterface $validator)
  8. {
  9. $author = new Author();
  10.  
  11. // ... do something to the $author object
  12.  
  13. $errors = $validator->validate($author);
  14.  
  15. if (count($errors) > 0) {
  16. /*
  17. * Uses a __toString method on the $errors variable which is a
  18. * ConstraintViolationList object. This gives us a nice string
  19. * for debugging.
  20. */
  21. $errorsString = (string) $errors;
  22.  
  23. return new Response($errorsString);
  24. }
  25.  
  26. return new Response('The author is valid! Yes!');
  27. }

If the $name property is empty, you will see the following errormessage:

  1. Object(App\Entity\Author).name:
  2. This value should not be blank

If you insert a value into the name property, the happy success messagewill appear.

Tip

Most of the time, you won't interact directly with the validatorservice or need to worry about printing out the errors. Most of the time,you'll use validation indirectly when handling submitted form data. Formore information, see how to validate Symfony forms.

You could also pass the collection of errors into a template:

  1. if (count($errors) > 0) {
  2. return $this->render('author/validation.html.twig', [
  3. 'errors' => $errors,
  4. ]);
  5. }

Inside the template, you can output the list of errors exactly as needed:

  1. {# templates/author/validation.html.twig #}
  2. <h3>The author has the following errors</h3>
  3. <ul>
  4. {% for error in errors %}
  5. <li>{{ error.message }}</li>
  6. {% endfor %}
  7. </ul>

Note

Each validation error (called a "constraint violation"), is represented bya ConstraintViolation object.

Configuration

Before using the Symfony validator, make sure it's enabled in the main configfile:

  • YAML
  1. # config/packages/framework.yaml
  2. framework:
  3. validation: { enabled: true }
  • XML
  1. <!-- config/packages/framework.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <container xmlns="http://symfony.com/schema/dic/services"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:framework="http://symfony.com/schema/dic/symfony"
  6. xsi:schemaLocation="http://symfony.com/schema/dic/services
  7. https://symfony.com/schema/dic/services/services-1.0.xsd
  8. http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
  9.  
  10. <framework:config>
  11. <framework:validation enabled="true"/>
  12. </framework:config>
  13. </container>
  • PHP
  1. // config/packages/framework.php
  2. $container->loadFromExtension('framework', [
  3. 'validation' => [
  4. 'enabled' => true,
  5. ],
  6. ]);

Besides, if you plan to use annotations to configure validation, replace theprevious configuration by the following:

  • YAML
  1. # config/packages/framework.yaml
  2. framework:
  3. validation: { enable_annotations: true }
  • XML
  1. <!-- config/packages/framework.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <container xmlns="http://symfony.com/schema/dic/services"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xmlns:framework="http://symfony.com/schema/dic/symfony"
  6. xsi:schemaLocation="http://symfony.com/schema/dic/services
  7. https://symfony.com/schema/dic/services/services-1.0.xsd
  8. http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
  9.  
  10. <framework:config>
  11. <framework:validation enable-annotations="true"/>
  12. </framework:config>
  13. </container>
  • PHP
  1. // config/packages/framework.php
  2. $container->loadFromExtension('framework', [
  3. 'validation' => [
  4. 'enable_annotations' => true,
  5. ],
  6. ]);

Tip

When using PHP, YAML, and XML files instead of annotations, Symfony looksfor by default in the config/validator/ directory, but you can configureother directories with the validation.mapping.paths option.

Constraints

The validator is designed to validate objects against constraints (i.e.rules). In order to validate an object, simply map one or more constraintsto its class and then pass it to the validator service.

Behind the scenes, a constraint is simply a PHP object that makes an assertivestatement. In real life, a constraint could be: 'The cake must not be burned'.In Symfony, constraints are similar: they are assertions that a conditionis true. Given a value, a constraint will tell you if that valueadheres to the rules of the constraint.

Supported Constraints

Symfony packages many of the most commonly-needed constraints:

Basic Constraints

These are the basic constraints: use them to assert very basic things aboutthe value of properties or the return value of methods on your object.

String Constraints

Comparison Constraints

Number Constraints

Date Constraints

Choice Constraints

File Constraints

Financial and other Number Constraints

Other Constraints

Constraint Configuration

Some constraints, like NotBlank,are simple whereas others, like the Choiceconstraint, have several configuration options available. Suppose that theAuthor class has another property called genre that defines theliterature genre mostly associated with the author, which can be set to either"fiction" or "non-fiction":

  • Annotations
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6.  
  7. class Author
  8. {
  9. /**
  10. * @Assert\Choice(
  11. * choices = { "fiction", "non-fiction" },
  12. * message = "Choose a valid genre."
  13. * )
  14. */
  15. private $genre;
  16.  
  17. // ...
  18. }
  • YAML
  1. # config/validator/validation.yaml
  2. App\Entity\Author:
  3. properties:
  4. genre:
  5. - Choice: { choices: [fiction, non-fiction], message: Choose a valid genre. }
  6. # ...
  • XML
  1. <!-- config/validator/validation.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
  6. https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
  7.  
  8. <class name="App\Entity\Author">
  9. <property name="genre">
  10. <constraint name="Choice">
  11. <option name="choices">
  12. <value>fiction</value>
  13. <value>non-fiction</value>
  14. </option>
  15. <option name="message">Choose a valid genre.</option>
  16. </constraint>
  17. </property>
  18.  
  19. <!-- ... -->
  20. </class>
  21. </constraint-mapping>
  • PHP
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6. use Symfony\Component\Validator\Mapping\ClassMetadata;
  7.  
  8. class Author
  9. {
  10. private $genre;
  11.  
  12. // ...
  13.  
  14. public static function loadValidatorMetadata(ClassMetadata $metadata)
  15. {
  16. // ...
  17.  
  18. $metadata->addPropertyConstraint('genre', new Assert\Choice([
  19. 'choices' => ['fiction', 'non-fiction'],
  20. 'message' => 'Choose a valid genre.',
  21. ]));
  22. }
  23. }

The options of a constraint can always be passed in as an array. Some constraints,however, also allow you to pass the value of one, "default", option in placeof the array. In the case of the Choice constraint, the choicesoptions can be specified in this way.

  • Annotations
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6.  
  7. class Author
  8. {
  9. /**
  10. * @Assert\Choice({"fiction", "non-fiction"})
  11. */
  12. private $genre;
  13.  
  14. // ...
  15. }
  • YAML
  1. # config/validator/validation.yaml
  2. App\Entity\Author:
  3. properties:
  4. genre:
  5. - Choice: [fiction, non-fiction]
  6. # ...
  • XML
  1. <!-- config/validator/validation.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
  6. https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
  7.  
  8. <class name="App\Entity\Author">
  9. <property name="genre">
  10. <constraint name="Choice">
  11. <value>fiction</value>
  12. <value>non-fiction</value>
  13. </constraint>
  14. </property>
  15.  
  16. <!-- ... -->
  17. </class>
  18. </constraint-mapping>
  • PHP
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6. use Symfony\Component\Validator\Mapping\ClassMetadata;
  7.  
  8. class Author
  9. {
  10. private $genre;
  11.  
  12. public static function loadValidatorMetadata(ClassMetadata $metadata)
  13. {
  14. // ...
  15.  
  16. $metadata->addPropertyConstraint(
  17. 'genre',
  18. new Assert\Choice(['fiction', 'non-fiction'])
  19. );
  20. }
  21. }

This is purely meant to make the configuration of the most common option ofa constraint shorter and quicker.

If you're ever unsure of how to specify an option, either check the namespaceSymfony\Component\Validator\Constraints for the constraint or play it safeby always passing in an array of options (the first method shown above).

Constraints in Form Classes

Constraints can be defined while building the form via the constraints optionof the form fields:

  1. public function buildForm(FormBuilderInterface $builder, array $options)
  2. {
  3. $builder
  4. ->add('myField', TextType::class, [
  5. 'required' => true,
  6. 'constraints' => [new Length(['min' => 3])]
  7. ])
  8. ;
  9. }

Constraint Targets

Constraints can be applied to a class property (e.g. name), a publicgetter method (e.g. getFullName()) or an entire class. Property constraintsare the most common and easy to use. Getter constraints allow you to specifymore complex validation rules. Finally, class constraints are intendedfor scenarios where you want to validate a class as a whole.

Properties

Validating class properties is the most basic validation technique. Symfonyallows you to validate private, protected or public properties. The nextlisting shows you how to configure the $firstName property of an Authorclass to have at least 3 characters.

  • Annotations
  1. // src/Entity/Author.php
  2.  
  3. // ...
  4. use Symfony\Component\Validator\Constraints as Assert;
  5.  
  6. class Author
  7. {
  8. /**
  9. * @Assert\NotBlank
  10. * @Assert\Length(min=3)
  11. */
  12. private $firstName;
  13. }
  • YAML
  1. # config/validator/validation.yaml
  2. App\Entity\Author:
  3. properties:
  4. firstName:
  5. - NotBlank: ~
  6. - Length:
  7. min: 3
  • XML
  1. <!-- config/validator/validation.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
  6. https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
  7.  
  8. <class name="App\Entity\Author">
  9. <property name="firstName">
  10. <constraint name="NotBlank"/>
  11. <constraint name="Length">
  12. <option name="min">3</option>
  13. </constraint>
  14. </property>
  15. </class>
  16. </constraint-mapping>
  • PHP
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6. use Symfony\Component\Validator\Mapping\ClassMetadata;
  7.  
  8. class Author
  9. {
  10. private $firstName;
  11.  
  12. public static function loadValidatorMetadata(ClassMetadata $metadata)
  13. {
  14. $metadata->addPropertyConstraint('firstName', new Assert\NotBlank());
  15. $metadata->addPropertyConstraint(
  16. 'firstName',
  17. new Assert\Length(["min" => 3])
  18. );
  19. }
  20. }

Getters

Constraints can also be applied to the return value of a method. Symfonyallows you to add a constraint to any public method whose name starts with"get", "is" or "has". In this guide, these types of methods are referred toas "getters".

The benefit of this technique is that it allows you to validate your objectdynamically. For example, suppose you want to make sure that a password fielddoesn't match the first name of the user (for security reasons). You cando this by creating an isPasswordSafe() method, and then asserting thatthis method must return true:

  • Annotations
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6.  
  7. class Author
  8. {
  9. /**
  10. * @Assert\IsTrue(message="The password cannot match your first name")
  11. */
  12. public function isPasswordSafe()
  13. {
  14. // ... return true or false
  15. }
  16. }
  • YAML
  1. # config/validator/validation.yaml
  2. App\Entity\Author:
  3. getters:
  4. passwordSafe:
  5. - 'IsTrue': { message: 'The password cannot match your first name' }
  • XML
  1. <!-- config/validator/validation.xml -->
  2. <?xml version="1.0" encoding="UTF-8" ?>
  3. <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
  6. https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
  7.  
  8. <class name="App\Entity\Author">
  9. <getter property="passwordSafe">
  10. <constraint name="IsTrue">
  11. <option name="message">The password cannot match your first name</option>
  12. </constraint>
  13. </getter>
  14. </class>
  15. </constraint-mapping>
  • PHP
  1. // src/Entity/Author.php
  2. namespace App\Entity;
  3.  
  4. // ...
  5. use Symfony\Component\Validator\Constraints as Assert;
  6. use Symfony\Component\Validator\Mapping\ClassMetadata;
  7.  
  8. class Author
  9. {
  10. public static function loadValidatorMetadata(ClassMetadata $metadata)
  11. {
  12. $metadata->addGetterConstraint('passwordSafe', new Assert\IsTrue([
  13. 'message' => 'The password cannot match your first name',
  14. ]));
  15. }
  16. }

Now, create the isPasswordSafe() method and include the logic you need:

  1. public function isPasswordSafe()
  2. {
  3. return $this->firstName !== $this->password;
  4. }

Note

The keen-eyed among you will have noticed that the prefix of the getter("get", "is" or "has") is omitted in the mappings for the YAML, XML and PHPformats. This allows you to move the constraint to a property with the samename later (or vice versa) without changing your validation logic.

Classes

Some constraints apply to the entire class being validated. For example,the Callback constraint is a genericconstraint that's applied to the class itself. When that class is validated,methods specified by that constraint are simply executed so that each canprovide more custom validation.

Final Thoughts

The Symfony validator is a powerful tool that can be leveraged toguarantee that the data of any object is "valid". The power behind validationlies in "constraints", which are rules that you can apply to properties orgetter methods of your object. And while you'll most commonly use the validationframework indirectly when using forms, remember that it can be used anywhereto validate any object.

Learn more