Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Book
  4. Validation
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • The Basics of Validation
    • Using the validator Service
    • Validation and Forms
  • Configuration
  • Constraints
    • Supported Constraints
    • Basic Constraints
    • String Constraints
    • Number Constraints
    • Date Constraints
    • Collection Constraints
    • File Constraints
    • Financial Constraints
    • Other Constraints
    • Constraint Configuration
  • Translation Constraint Messages
  • Constraint Targets
    • Properties
    • Getters
    • Classes
  • Validation Groups
  • Group Sequence
    • Group Sequence Providers
  • Validating Values and Arrays
  • Final Thoughts
  • Learn more from the Cookbook

Validation

Edit this page

Warning: You are browsing the documentation for Symfony 2.2, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

Validation

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

Symfony2 ships with a Validator component that makes this task easy and transparent. This component is based on the JSR303 Bean Validation specification.

The Basics of Validation

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

1
2
3
4
5
6
7
// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

class Author
{
    public $name;
}

So far, this is just an ordinary class that serves some purpose inside your application. The goal of validation is to tell you whether or not the data of an object is valid. For this to work, you'll configure a list of rules (called constraints) that the object must follow in order to be valid. These rules can be specified via a number of different formats (YAML, XML, annotations, or PHP).

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

  • YAML
  • Annotations
  • XML
  • PHP
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
11
12
// 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
15
// 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

Protected and private properties can also be validated, as well as "getter" methods (see Validation).

Using the validator Service

Next, to actually validate an Author object, use the validate method on the validator service (class Validator). The job of the validator is easy: to read the constraints (i.e. rules) of a class and verify whether or not the data on the object satisfies those constraints. If validation fails, a non-empty list of errors (class ConstraintViolationList) is returned. Take this simple example from inside a controller:

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
// ...
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) {
        /*
         * Uses a __toString method on the $errors variable which is a
         * ConstraintViolationList object. This gives us a nice string
         * for debugging
         */
        $errorsString = (string) $errors;

        return new Response($errorsString);
    }

    return new Response('The author is valid! Yes!');
}

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

1
2
Acme\BlogBundle\Author.name:
    This value should not be blank

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

Tip

Most of the time, you won't interact directly with the validator service or need to worry about printing out the errors. Most of the time, you'll use validation indirectly when handling submitted form data. For more information, see the Validation.

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

1
2
3
4
5
if (count($errors) > 0) {
    return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
        'errors' => $errors,
    ));
}

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

  • Twig
  • PHP
1
2
3
4
5
6
7
{# 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
<!-- 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

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

Validation and Forms

The validator service can be used at any time to validate any object. In reality, however, you'll usually work with the validator indirectly when working with forms. Symfony's form library uses the validator service internally to validate the underlying object after values have been submitted and bound. The constraint violations on the object are converted into FieldError objects that can easily be displayed with your form. The typical form submission workflow looks like the following from inside a controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ...
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);

    if ($request->isMethod('POST')) {
        $form->bind($request);

        if ($form->isValid()) {
            // the validation passed, do something with the $author object

            return $this->redirect($this->generateUrl(...));
        }
    }

    return $this->render('BlogBundle:Author:form.html.twig', array(
        'form' => $form->createView(),
    ));
}

Note

This example uses an AuthorType form class, which is not shown here.

For more information, see the Forms chapter.

Configuration

The Symfony2 validator is enabled by default, but you must explicitly enable annotations if you're using the annotation method to specify your constraints:

  • YAML
  • XML
  • PHP
1
2
3
# app/config/config.yml
framework:
    validation: { enable_annotations: true }
1
2
3
4
5
6
7
8
9
10
11
12
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:framework="http://symfony.com/schema/dic/symfony"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
                        http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

    <framework:config>
        <framework:validation enable-annotations="true" />
    </framework:config>
</container>
1
2
3
4
5
6
// app/config/config.php
$container->loadFromExtension('framework', array(
    'validation' => array(
        'enable_annotations' => true,
    ),
));

Constraints

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

Behind the scenes, a constraint is simply a PHP object that makes an assertive statement. In real life, a constraint could be: "The cake must not be burned". In Symfony2, constraints are similar: they are assertions that a condition is true. Given a value, a constraint will tell you whether or not that value adheres to the rules of the constraint.

Supported Constraints

Symfony2 packages a large number of the most commonly-needed constraints:

Basic Constraints

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

  • NotBlank
  • Blank
  • NotNull
  • Null
  • True
  • False
  • Type

String Constraints

  • Email
  • MinLength
  • MaxLength
  • Length
  • Url
  • Regex
  • Ip

Number Constraints

  • Max
  • Min
  • Range

Date Constraints

  • Date
  • DateTime
  • Time

Collection Constraints

  • Choice
  • Collection
  • Count
  • UniqueEntity
  • Language
  • Locale
  • Country

File Constraints

  • File
  • Image

Financial Constraints

  • CardScheme
  • Luhn

Other Constraints

  • Callback
  • All
  • UserPassword
  • Valid

You can also create your own custom constraints. This topic is covered in the "How to create a Custom Validation Constraint" article of the cookbook.

Constraint Configuration

Some constraints, like NotBlank, are simple whereas others, like the Choice constraint, have several configuration options available. Suppose that the Author class has another property, gender that can be set to either "male" or "female":

  • YAML
  • Annotations
  • XML
  • PHP
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
17
18
// src/Acme/BlogBundle/Entity/Author.php

// ...
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\Choice;

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.',
        )));
    }
}

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 place of the array. In the case of the Choice constraint, the choices options can be specified in this way.

  • YAML
  • Annotations
  • XML
  • PHP
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
11
12
// 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
14
15
16
17
18
// 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'))
        );
    }
}

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

If you're ever unsure of how to specify an option, either check the API documentation for the constraint or play it safe by always passing in an array of options (the first method shown above).

Translation Constraint Messages

For information on translating the constraint messages, see Translations.

Constraint Targets

Constraints can be applied to a class property (e.g. name) or a public getter method (e.g. getFullName). The first is the most common and easy to use, but the second allows you to specify more complex validation rules.

Properties

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

  • YAML
  • Annotations
  • XML
  • PHP
1
2
3
4
5
6
7
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
        firstName:
            - NotBlank: ~
            - Length:
                min: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
// Acme/BlogBundle/Entity/Author.php

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Author
{
    /**
     * @Assert\NotBlank()
     * @Assert\Length(min = "3")
     */
    private $firstName;
}
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="firstName">
            <constraint name="NotBlank" />
            <constraint name="Length">
                <option name="min">3</option>
            </constraint>
        </property>
    </class>
</constraint-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/Acme/BlogBundle/Entity/Author.php

// ...
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Length;

class Author
{
    private $firstName;

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('firstName', new NotBlank());
        $metadata->addPropertyConstraint(
            'firstName',
            new Length(array("min" => 3)));
    }
}

Getters

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

The benefit of this technique is that it allows you to validate your object dynamically. For example, suppose you want to make sure that a password field doesn't match the first name of the user (for security reasons). You can do this by creating an isPasswordLegal method, and then asserting that this method must return true:

  • YAML
  • Annotations
  • XML
  • PHP
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
14
15
// 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
9
10
11
12
13
14
<!-- 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">
        <getter property="passwordLegal">
            <constraint name="True">
                <option name="message">The password cannot match your first name</option>
            </constraint>
        </getter>
    </class>
</constraint-mapping>
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\True;

class Author
{
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addGetterConstraint('passwordLegal', new True(array(
            'message' => 'The password cannot match your first name',
        )));
    }
}

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

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

Note

The keen-eyed among you will have noticed that the prefix of the getter ("get" or "is") is omitted in the mapping. This allows you to move the constraint to a property with the same name 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 generic constraint that's applied to the class itself. When that class is validated, methods specified by that constraint are simply executed so that each can provide more custom validation.

Validation Groups

So far, you've been able to add constraints to a class and ask whether or not that class passes all of the defined constraints. In some cases, however, you'll need to validate an object against only some of the constraints on that class. To do this, you can organize each constraint into one or more "validation groups", and then apply validation against just one group of constraints.

For example, suppose you have a User class, which is used both when a user registers and when a user updates their contact information later:

  • YAML
  • Annotations
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
    properties:
        email:
            - Email: { groups: [registration] }
        password:
            - NotBlank: { groups: [registration] }
            - Length: { min: 7, groups: [registration] }
        city:
            - Length:
                min: 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\Length(min=7, groups={"registration"})
    */
    private $password;

    /**
    * @Assert\Length(min = "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
27
28
29
30
31
32
33
34
<!-- 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\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="Length">
                <option name="min">7</option>
                <option name="groups">
                    <value>registration</value>
                </option>
            </constraint>
        </property>
        <property name="city">
            <constraint name="Length">
                <option name="min">7</option>
            </constraint>
        </property>
    </class>
</constraint-mapping>
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
28
29
// 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\Length;

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 Length(array(
            'min'  => 7,
            'groups' => array('registration')
        )));

        $metadata->addPropertyConstraint(
            'city',
            Length(array("min" => 3)));
    }
}

With this configuration, there are two validation groups:

  • User - contains the constraints that belong to no other group, and is considered the Default group. (This group is useful for Validation);
  • registration - contains the constraints on the email and password fields only.

To tell the validator to use a specific group, pass one or more group names as the second argument to the validate() method:

1
$errors = $validator->validate($author, array('registration'));

If no groups are specified, all constraints that belong in group Default will be applied.

Of course, you'll usually work with validation indirectly through the form library. For information on how to use validation groups inside forms, see Forms.

Group Sequence

In some cases, you want to validate your groups by steps. To do this, you can use the GroupSequence feature. In the case, an object defines a group sequence, and then the groups in the group sequence are validated in order.

Tip

Group sequences cannot contain the group Default, as this would create a loop. Instead, use the group {ClassName} (e.g. User).

For example, suppose you have a User class and want to validate that the username and the password are different only if all other validation passes (in order to avoid multiple error messages).

  • YAML
  • Annotations
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\User:
    group_sequence:
        - User
        - Strict
    getters:
        passwordLegal:
            - "True":
                message: "The password cannot match your username"
                groups: [Strict]
    properties:
        username:
            - NotBlank: ~
        password:
            - NotBlank: ~
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
28
29
// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Assert\GroupSequence({"User", "Strict"})
 */
class User implements UserInterface
{
    /**
    * @Assert\NotBlank
    */
    private $username;

    /**
    * @Assert\NotBlank
    */
    private $password;

    /**
     * @Assert\True(message="The password cannot match your username", groups={"Strict"})
     */
    public function isPasswordLegal()
    {
        return ($this->username !== $this->password);
    }
}
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/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\User">
        <property name="username">
            <constraint name="NotBlank" />
        </property>
        <property name="password">
            <constraint name="NotBlank" />
        </property>
        <getter property="passwordLegal">
            <constraint name="True">
                <option name="message">The password cannot match your username</option>
                <option name="groups">
                    <value>Strict</value>
                </option>
            </constraint>
        </getter>
        <group-sequence>
            <value>User</value>
            <value>Strict</value>
        </group-sequence>
    </class>
</constraint-mapping>
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
28
29
30
// src/Acme/BlogBundle/Entity/User.php
namespace Acme\BlogBundle\Entity;

use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;

class User
{
    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint(
            'username',
            new Assert\NotBlank()
        );
        $metadata->addPropertyConstraint(
            'password',
            new Assert\NotBlank()
        );

        $metadata->addGetterConstraint(
            'passwordLegal',
            new Assert\True(array(
                'message' => 'The password cannot match your first name',
                'groups'  => array('Strict'),
            ))
        );

        $metadata->setGroupSequence(array('User', 'Strict'));
    }
}

In this example, it will first validate all constraints in the group User (which is the same as the Default group). Only if all constraints in that group are valid, the second group, Strict, will be validated.

Group Sequence Providers

Imagine a User entity which can be a normal user or a premium user. When it's a premium user, some extra constraints should be added to the user entity (e.g. the credit card details). To dynamically determine which groups should be activated, you can create a Group Sequence Provider. First, create the entity and a new constraint group called Premium:

  • YAML
  • Annotations
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
# src/Acme/DemoBundle/Resources/config/validation.yml
Acme\DemoBundle\Entity\User:
    properties:
        name:
            - NotBlank
        creditCard:
            - CardScheme
                schemes: [VISA]
                groups: [Premium]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Acme/DemoBundle/Entity/User.php
namespace Acme\DemoBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;

class User
{
    // ...

    /**
     * @Assert\NotBlank()
     */
    private $name;

    /**
     * @Assert\CardScheme(
     *     schemes={"VISA"},
     *     groups={"Premium"},
     * )
     */
    private $creditCard;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- src/Acme/DemoBundle/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\DemoBundle\Entity\User">
        <property name="name">
            <constraint name="NotBlank" />
        </property>

        <property name="creditCard">
            <constraint name="CardScheme">
                <option name="schemes">
                    <value>VISA</value>
                </option>
                <option name="groups">
                    <value>Premium</value>
                </option>
            </constraint>
        </property>
    </class>
</constraint-mapping>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/Acme/DemoBundle/Entity/User.php
namespace Acme\DemoBundle\Entity;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;

class User
{
    private $name;
    private $creditCard;

    // ...

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('name', new Assert\NotBlank());
        $metadata->addPropertyConstraint('creditCard', new Assert\CardScheme(
            'schemes' => array('VISA'),
            'groups'  => array('Premium'),
        ));
    }
}

Now, change the User class to implement GroupSequenceProviderInterface and add the getGroupSequence(), which should return an array of groups to use. Also, add the @Assert\GroupSequenceProvider annotation to the class. If you imagine that a method called isPremium returns true if the user is a premium member, then your code might look like this:

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
// src/Acme/DemoBundle/Entity/User.php
namespace Acme\DemoBundle\Entity;

// ...
use Symfony\Component\Validator\GroupSequenceProviderInterface;

/**
 * @Assert\GroupSequenceProvider
 * ...
 */
class User implements GroupSequenceProviderInterface
{
    // ...

    public function getGroupSequence()
    {
        $groups = array('User');

        if ($this->isPremium()) {
            $groups[] = 'Premium';
        }

        return $groups;
    }
}

Validating Values and Arrays

So far, you've seen how you can validate entire objects. But sometimes, you just want to validate a simple value - like to verify that a string is a valid email address. This is actually pretty easy to do. From inside a controller, it looks like this:

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
use Symfony\Component\Validator\Constraints\Email;
// ...

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 something with the error
    }

    // ...
}

By calling validateValue on the validator, you can pass in a raw value and the constraint object that you want to validate that value against. A full list of the available constraints - as well as the full class name for each constraint - is available in the constraints reference section .

The validateValue method returns a ConstraintViolationList object, which acts just like an array of errors. Each error in the collection is a ConstraintViolation object, which holds the error message on its `getMessage` method.

Final Thoughts

The Symfony2 validator is a powerful tool that can be leveraged to guarantee that the data of any object is "valid". The power behind validation lies in "constraints", which are rules that you can apply to properties or getter methods of your object. And while you'll most commonly use the validation framework indirectly when using forms, remember that it can be used anywhere to validate any object.

Learn more from the Cookbook

  • How to create a Custom Validation Constraint
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Online exam, become Symfony certified today

    Online exam, become Symfony certified today

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Robert Parker, a Symfony contributor

    Thanks Robert Parker (@yamiko_ninja) for being a Symfony contributor

    5 commits • 136 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Algolia