Validation Constraints with @Assert

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

When you talk about validation, what you're really talking about is business rules validation. That's where you tell Symfony that the title is required and needs to be a certain length, or that some field should be a valid email address, or that the password must contain 2 upper case letters, 2 lower case letters, 3 unicode characters and at least 4 emojis. It's about making the data constrain to your application's rules.

Adding your First Assert Annotation

Symfony's validation is kinda interesting because you do not apply the validation rules to the form. Nope, you apply them to your class via annotations. Check this out: I want the title field to be required. To do that, type @NotBlank and hit tab to autocomplete to @Assert\NotBlank. Because I have the PHP annotations plugin installed, when I auto-completed that, it added a use statement on top that we need: use Symfony\Component\Validator\Constraints as Assert.

... lines 1 - 11
use Symfony\Component\Validator\Constraints as Assert;
... lines 13 - 16
class Article
{
... lines 19 - 27
/**
... line 29
* @Assert\NotBlank()
*/
private $title;
... lines 33 - 255
}

Without doing anything else, refresh the form - the title field is empty. Yes! That's our error!

This value should not be blank.

To customize that, add a message key to the annotation:

Get creative and think of a title!

... lines 1 - 27
/**
... line 29
* @Assert\NotBlank(message="Get creative and think of a title!")
*/
private $title;
... lines 33 - 257

Try it again - refresh and... nice!

The Built-in Validation Constraints

On the docs, click to go back to the documentation homepage. Then, under guides, find the "Validation" guide. Just like with the form fields, there are a bunch of built-in validation constraints that... can help you validate almost anything! And... just like with form field types, each validation constraint has different options that control its behavior.

For example - check out Length: you can set the min length with the min option, or max with max. Control their error messages with minMessage and maxMessage.

Oh, another way to see what the options are is to remember that every annotation has a concrete PHP class behind it. Thanks to the PHP annotations plugin, I can hold Command or Ctrl and click the annotation to jump to that class.

Nice! Every property becomes an option that you can pass to the annotation. We'll see this again later when we create our own custom validation constraint.

Anyways, we won't talk too much about validation constraints because... they're honestly pretty easy: it's usually a matter of finding which validation constraint you need and the options to pass to it.

The Callback Constraint

Oh, but there is one really cool constraint called Callback. This is the tool when you need to go rogue and do something totally custom. Check it out: create a method in your class and add @Assert\Callback() above it. Then, during validation, Symfony will call your method!

Let's copy this, find our Article class, go all the way to the bottom, and paste. Oh, I need to retype the end of ExecutionContextInterface and auto-complete it to get the use statement. Then, inside... it's awesome! We can do whatever we want!

... lines 1 - 12
use Symfony\Component\Validator\Context\ExecutionContextInterface;
... lines 14 - 17
class Article
{
... lines 20 - 257
/**
* @Assert\Callback
*/
public function validate(ExecutionContextInterface $context, $payload)
{
... lines 263 - 267
}
}

Let's make sure that the title of this Article doesn't contain the string the borg... cause they're scary. So, if stripos() of $this->getTitle() and the borg does not equal false... error! To create the error, use $context->buildViolation():

Um.. the Borg kinda makes us nervous

Apparently so nervous that I typed "the Bork" instead! Resistance to typos is futile...

Next, choose which field to attach the error to with ->atPath('title') and finish with ->addViolation(). That's it!

... lines 1 - 260
public function validate(ExecutionContextInterface $context, $payload)
{
if (stripos($this->getTitle(), 'the borg') !== false) {
$context->buildViolation('Um.. the Bork kinda makes us nervous')
->atPath('title')
->addViolation();
}
}
... lines 269 - 270

Go back to our form, write an article about how you really want to join the borg and Create!

Got it! Custom validation logic with a custom error.

Next: let's talk a little more about how we can control the rendering of these fields. Because, right now, we're just sort of rendering them all at once... without much control over their look and feel.

Leave a comment!

  • 2020-06-05 Victor Bocharsky

    Hey Med,

    Try to set the error message for the 2nd field only, the it should look good I think. Otherwise, you can think of splitting this error message into 2 separate about first name and last name. As an alternative, you can skip atPath() call and apply this error to the whole form instead of the specific field. This is more relevant for what you're trying to do - a general error for the whole form. If none of this fits you - go to the template and render your form yourself with form_error(), form_label(), form_widget() Twig functions. This will give you flexibility and you will render the error wherever you want.

    I hope this helps!

    Cheers!

  • 2020-06-04 Med Drh

    Thank you for your answer, I want to display the same error message for two different fields.
    If I put a second violation I would have two identical messages for the two fields and I want only one error message: /
    Message error : FirstName or lastName can't be null

  • 2020-06-04 Victor Bocharsky

    Hey Med,

    I'm afraid you cannot do this. The 2nd call of atPath() will override the previous value. What are you trying to achieve? Display the same message for a few fields? Try to build another second violation for this, like:


    $errorMessage = '%Your error message here%';
    $context->buildViolation($errorMessage)
    ->atPath('firstName')
    ->addViolation();
    $context->buildViolation($errorMessage)
    ->atPath('lastName')
    ->addViolation();

    Just put your error message on a variable and use it to avoid duplications if needed.

    I hope this helps!

    Cheers!

  • 2020-06-04 Victor Bocharsky

    Hey Vali,

    Good question! Actually, IIRC they should not override each other, they should "summarize". Like Validator will check both validation constraint you declared in your entity and in form type. You can easily double-check this ;)

    Cheers!

  • 2020-06-03 Vali Leo

    Hello,
    Will entity validation rules be overwritten by form validation rules?

  • 2020-06-03 Med Drh

    Hello, How to add two atPath () ?, I tried to put -> atPath ('firstName')->atPath('lastName').is working just for one field :/

  • 2020-06-03 Graham Dodgson

    Ah you needed to add 'required' => false - perfect!

  • 2020-06-03 Graham Dodgson

    So following this I can't seem to switch off the html5 validation to get the custom message fo blanl - I just get the browser instructing value cannot be empty

  • 2019-12-30 Victor Bocharsky

    Hey Coder,

    Yes, you can apply validation rules to entities or to form types. General rules better apply to entities because those rules will work for all forms. But if you want to apply a specific rule for a specific form - you can do it too in the form type.

    Cheers!

  • 2019-12-29 Coder

    "you do not apply the validation rules to the form" - you can apply to the form as I understand https://symfony.com/doc/4.4...

  • 2019-11-25 Virgile Sahaguian

    Hey, don't forget to get the "required" field blank (with the inspector) or make your <input> novalidate into html :p

  • 2019-11-14 Vladimir Sadicov

    Hey @stephansav,

    Are you just refreshing page? or re-submitting form? You should submit form again, because assertion works only when form is submitted

    Cheers!

  • 2019-11-13 stephansav

    Hi,
    I put @Assert\NotBlank() on the private $title property of the Article class but when I refresh I haven't the error message like you: "Error the value should not be blank". Can you explain me how I can solve that?

  • 2019-10-18 Diego Aguiar

    Hey Marine GREGOIRE

    You will need to implement an endpoint where you can submit the form's data, validate the fields and finally return the form's HTML. It would be almost the same logic as you already do but instead of doing "some" logic, such endpoint will always return the rendered form. Does it make any sense to you?

    Cheers!

  • 2019-10-17 Marine GREGOIRE

    Hi everyone !

    I did @Assert on all my entities and that my form works. But what if I wanna use Ajax to display the data in a modal before submitting them ? How can I make the error messages appear then ? (Because I did it but since Ajax calls, reload is not happening and thus no error message are displayed. Any hint ?
    I am lost :'(

    Thanx

  • 2019-08-22 Diego Aguiar

    Hey orallo

    You are using the wrong constraint for your goal. Give it a look on the docs: https://symfony.com/doc/cur...
    that Unique constraint is for collections, the one you are looking for is UniqueEntity and it applies over the class. I'll leave you the docs here: https://symfony.com/doc/cur... but if you have more doubts, let us know :)

    Cheers!

  • 2019-08-22 orallo

    Hi everyone!

    I´m just getting started with symfony and I'm trying out this Assert validation stuff...

    I'm trying to make a field not empty and unique name with the following code:

    class Portals
    {
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;

    /**
    * @ORM\Column(name="name", type="string", length=255)
    * @Assert\NotBlank(message="El portal debe tener un nombre.")
    * @Assert\Unique(message="Este nombre ya existe")
    *
    */

    private $name;

    And when I submit the form I get an "This value should be of type array|IteratorAggregate" no matter what I type in the field, it doesn't matter if the name already exists on the database or not, it just never goes through! is this a bug on 4.3??? Am I missing something???

    Thanks for your help in advance!
    -Orallo

  • 2019-08-15 Victor Bocharsky

    Hey Krzysztof,

    What do you mean? What your validator exactly? Fairly speaking, I'm not sure there's any priorities in applying validation constraints, maybe try to change their order? They might be applied in order how they are declared.

    I hope this helps!

    Cheers!

  • 2019-08-14 Krzysztof Krakowiak

    Thanks for letting me know!

  • 2019-08-14 Krzysztof Krakowiak

    Thanks Victor, but what about validation priority? Can my validator be executed first? I was unable to find any additional info about this new feature.

  • 2019-08-13 Victor Bocharsky

    Hey Krzysztof,

    Oh, thanks for this ping! Our teammate Diego replied to you the next day, but somehow that message get lost, I restored it from the history, see the answer here: https://symfonycasts.com/sc...

    We're sorry about it!

    Cheers!

  • 2019-08-13 Victor Bocharsky

    Hey Krzysztof

    For translating validation messages you can follow this guide: https://symfony.com/doc/cur...
    And for disabling the auto validation system. Hmm, If I recall correctly it should be disabled by default but I believe you can disable it by changing the "framework.validation" configuration


    // config/packages/validation.yaml
    framework:
    validation:
    auto_mapping: []

    Give it a try and let us know if it worked.

    Cheers!
    Diego

  • 2019-08-13 Krzysztof Krakowiak

    Guys, any news on above?

  • 2019-08-05 Victor Bocharsky

    Hey Avraham,

    Great, glad it works for you. This is an awesome new feature indeed.

    Cheers!

  • 2019-08-02 Avraham Markov

    Hey Victor!
    Uncommenting

    auto_mapping:
    App\Entity\: []

    from validator.yaml enabled Automatic validation!!!

    Github discussion say this caused issue with forms
    https://github.com/symfony/...

    Yet Automatic Validation works.

    Thank you very much!

  • 2019-08-02 Victor Bocharsky

    Hey Avraham,

    First of all, you need to upgrade your Symfony application to the latest 4.3 version, the version we use in this screencasts does not support it yet. Then, follow the instructions in that blog post you linked. And finally, you would need to enable this feature in your config/packages/validator.yaml like:


    framework:
    validation:
    email_validation_mode: html5

    # Enables validator auto-mapping support.
    # For instance, basic validation constraints will be inferred from Doctrine's metadata.
    auto_mapping:
    App\Entity\: []

    And then it should work I think. But it has an issue IIRC, you can follow this thread to get more information why it's not enabled by default:
    https://github.com/symfony/...

    I hope this helps!

    Cheers!

  • 2019-08-01 Avraham Markov

    Hello!
    Also regarding Automatic validation as described in link bellow

    https://symfony.com/blog/ne...

    Can you please explain how to make it work.

    I have symfony 4.3 application, currently create Entities,

    really do not want to add Validator annotations manually.

    Lets say I have a field notNull

    /**

    * @var string

    * @ORM\Column(name="title", type="string", length=10, nullable=false)

    */
    private $title;

    and in controller
    leaving title property empty and validating

    $errors = $validator->validate($post);
    finds no errors.

    Thanks!

    Avraham

  • 2019-07-31 Krzysztof Krakowiak

    Hello Guys I need your help, recently we have updated to Symfony 4.3 where new feature was introduced for auto validation.
    I now I have new issues... or I need some extra explanation or there is a bug...
    In my entity I have such definition for a property:


    /**
    * @ORM\Column(type="string", length=128)
    * @Assert\NotBlank()
    */
    private $tag;


    public function getTag(): ?string
    {
    return $this->tag;
    }


    public function setTag(string $tag): self
    {
    $this->tag = $tag;
    return $this;
    }

    With such setup like above when I edit entity and submit empty value I got following error: "This value should be of type string." previously I had (Symfony 4.2): "This value should not be blank."

    But when I create new entity I get correct error message, somehow behaviour for create and edit is different in Symfony 4.3?

    Why my validators do not have priority, why these automatic ones are executed first?

    Also I have noticed that if I will remove type (string) form setter then this works and I get correct message. Should I then remove these from all my entities?

    How can I overwrite this message automatic message: This value should be of type string.

    How I can disable this functionality in my project?