Buy

Beautiful Form Validation

Guess what! Server-side validation is really, really fun. Google for Symfony validation, and find the book chapter.

There is one weird thing about validation... which I love. Here it is: you don't apply validation to your form. Nope, there will be no validation code inside of GenusFormType. Instead, you add validation to the class that is bound to your form. When the form is submitted, it automatically reads those validation rules and uses them.

Validation is added with annotations. So copy the use statement from the code block, find Genus and paste it on top:

141 lines src/AppBundle/Entity/Genus.php
... lines 1 - 6
use Symfony\Component\Validator\Constraints as Assert;
... lines 8 - 141

The Giant List of Constraints

Good start! Next, we'll add validation rules - called constraints - above each property. On the left side bar, find the "Constraints" link.

Check out this menu of validation rules: NotBlank, NotNull, Email, Length, Regex... so many things! Pretty much anything you can dream up is inside of this list.

Let's start with an easy one: above the name property, add @Assert\NotBlank:

141 lines src/AppBundle/Entity/Genus.php
... lines 1 - 12
class Genus
{
... lines 15 - 21
/**
* @Assert\NotBlank()
... line 24
*/
private $name;
... lines 27 - 139
}

Without doing anything else, refresh. Boom! Validation error. And, it looks nice.

Let's add some more. For subFamily - that should be required, so add @NotBlank:

145 lines src/AppBundle/Entity/Genus.php
... lines 1 - 12
class Genus
{
... lines 15 - 27
/**
* @Assert\NotBlank()
... lines 30 - 31
*/
private $subFamily;
... lines 34 - 143
}

For speciesCount, add @NotBlank again:

145 lines src/AppBundle/Entity/Genus.php
... lines 1 - 12
class Genus
{
... lines 15 - 34
/**
* @Assert\NotBlank()
... lines 37 - 38
*/
private $speciesCount;
... lines 41 - 143
}

But in addition to that, we want speciesCount to be a positive number: we don't want some funny biologist entering negative 10.

Constraint Options

On the constraints list, there's one called Range. Check that out.

Ok cool: just like the form field types, you can pass options to the constraints. The Range constraint has several: min, max, minMessage and maxMessage. Add @Assert\Range with min=0 and minMessage="Negative species! Come on...":

145 lines src/AppBundle/Entity/Genus.php
... lines 1 - 12
class Genus
{
... lines 15 - 34
/**
* @Assert\NotBlank()
* @Assert\Range(min=0, minMessage="Negative species! Come on...")
... line 38
*/
private $speciesCount;
... lines 41 - 143
}

Ok, let's finish up. It's ok if funFact is empty - so don't add anything there. The same is true for isPublished: we could add a constraint to make sure this is a boolean, but the sanity validation on the form already takes care of that.

Finally, let's make sure firstDiscoveredAt is also NotBlank:

145 lines src/AppBundle/Entity/Genus.php
... lines 1 - 12
class Genus
{
... lines 15 - 51
/**
* @Assert\NotBlank()
... line 54
*/
private $firstDiscoveredAt;
... lines 57 - 143
}

Ok, refresh! Leave everything blank and put -10 for the number of species. I love it!

Leave a comment!

  • 2016-11-23 Victor Bocharsky

    Hey somecallmetim27 ,

    Yes, Symfony docs were rewritten a lot. Thanks for sharing it ;)

    Cheers!

  • 2016-11-22 somecallmetim27

    URL for Validation doc has changed. https://symfony.com/doc/curren...

    In case anyone gets lost :)

  • 2016-11-22 Victor Bocharsky

    Hi Terry,

    Yes, it's possible too: use `validation_groups` option for it like in the next example:


    $form = $this->createFormBuilder($task)
    ->add('task', TextType::class, [
    'validation_groups' => ['YourValidationGroup', 'AnotherValidationGroup'],
    ])
    ->add('dueDate', DateType::class)
    ->add('save', SubmitType::class, array('label' => 'Create Post'))
    ->getForm();

    Cheers!

  • 2016-11-22 Terry Caliendo

    Thanks much! ...I started that tutorial but hadn't gotten that far yet :)

    Also, just to follow up, that tutorial is using a Form Class. Is there a way to add Validation Groups with the simple form generation (below)? Or is this starting to get into one of the many reasons why one would need to switch to creating a Form Class?

    $form = $this->createFormBuilder($task)
    ->add('task', TextType::class)
    ->add('dueDate', DateType::class)
    ->add('save', SubmitType::class, array('label' => 'Create Post'))
    ->getForm();

  • 2016-11-20 weaverryan

    Yo Terry!

    Great question, and (fortunately) a simple answer! Check out validation groups: https://knpuniversity.com/scre... - it should be exactly what you need :).

    Cheers!

  • 2016-11-18 Terry Caliendo

    Say I have a form field where sometimes it can be blank and other times it needs to *not* be blank. For instance, say an admin can set the field to blank but a regular user can't. That would be two different annotations on the same Entity property. How might I go about accomplishing this dual need for a particular Entity property validation?

  • 2016-11-14 Victor Bocharsky

    Hi Terry,

    Good research! Yes, use validator service for it.

    Cheers!

  • 2016-11-14 Terry Caliendo

    Took a while, but I found an answer here:
    http://stackoverflow.com/quest...

  • 2016-11-14 Terry Caliendo

    Again... not exactly sure where to ask this but its similar to validation... Is there a way to ask symphony to validate a value without using the form validation service? Say for instance I'm reading input from a csv file and I want to validate if a field contains a valid date or valid email address.

  • 2016-10-21 weaverryan

    That's ok! It's a good thing to do wrong once :). And you did the right thing by checking out the profiler. Since it couldn't find the correct field to assign to, it "bubbles" to the top level. If I remember correctly, I *think* it might still tell you the "path" of where the error is coming from in the profiler to help. But anyways, you got it - nice debugging!

  • 2016-10-20 jabbergabber

    Hi Ryan

    My validation messages do not appear on the form - they are captured in the profiler but no sign of them on the form itself ??

    Doh! - my mistake - my form field names were not the same as the names in my associated class - inconsistent uppercase/lowercase naming convention - must try harder ... back of the class.

  • 2016-10-03 Maksym Minenko

    I hope there'll be a tutorial on *that*. :)

  • 2016-09-05 weaverryan

    Definitely :). It will technically make your tests a *little* bit slower... but you're also testing more (e.g. that the form points to the correct location and contains the token).

    Cheers!

  • 2016-09-02 Diego Aguiar

    Hi Ryan, thanks for your answer

    I was trying to save me a step by just calling the request method, like if it were an API call, but I guess it would be better to use your approach and actually submit the delete form

  • 2016-09-02 weaverryan

    Hey Diego!

    Hmm. So usually, CSRF protection in a function test isn't a problem, because you're actually pressing "submit" on a form, and since that form has a CSRF token embedded in it, it works. This is also the "best" way because you're really testing your true setup - not trying to fake anything.

    In your case, what's causing you to need to send a DELETE request directly to a URL? I think we could dig and get you an answer to this - i.e. using Symfony to actually fetch/fake what the token *should* be, then send it with the request - but let's see if we need to go down that road first :).

    Cheers!

  • 2016-09-02 Diego Aguiar

    Hello Ryan!

    I've been struggling in finding a way to disable CSRF protection when doing functional testing, I just can't set to false csrf_protection option in config_test.yml because I'm using FOS_USER Bundle and it has a dependency on the service (program crashes if I do that)
    Do you know any other way to bypass the protection ?

    What I'm trying to do is a DELETE request from a client instance

    $client->request("DELETE", $deletePath);

    Thanks in advance :]

  • 2016-08-11 weaverryan

    Awesome! Good luck and keep killing it!

  • 2016-08-11 Deividas Pekunas

    OH MY GOD!! THANK YOU! Really appreciate your help! That was the problem.

    One more time big thanks. :)

  • 2016-08-11 weaverryan

    Excellent! I don't see any fundamental mistakes, and you're actually doing something a little bit complex, with the embedded forms. So, congrats :). I think the problem is that the PersonalPersonal class is the "top-level" class that's being validated. Then, the PersonalMainInfo is "embedded" inside of it. The validation system doesn't automatically validate child objects like this - but if you add @Assert\Valid (http://symfony.com/doc/current... above the personMainInfo property in PersonalPersonal, it should fix the problem.

    Let me know if it works! Cheers!

  • 2016-08-11 Deividas Pekunas

    Wow so quick respone!
    http://pastebin.com/U5D6Y0Ap Here are some parts of my code.

    EDIT: If i've done some painfull fundumental mistakes please let me know! I'm new on Symfony. (Few days only)

  • 2016-08-11 weaverryan

    Hi Deividas!

    Oh no! Hmm, could you post your annotation code in your entity class? And also the code in your controller? Usually, this either works, or you do something wrong and you get an error - I can't think of very many ways that it would just *not* actually apply validation correctly. Also, you could experiment by adding the Callback constraint to your class (http://symfony.com/doc/current... and then add a die() statement in your callback function to see if Symfony sees (and calls) this constraint.

    Either way, I'm sure we'll figure it out :).

    Cheers!

  • 2016-08-11 Deividas Pekunas

    Hey i really need help. My annotation validation not working. $form->isValid always return true.. What can be possibly wrong in my project?

  • 2016-06-18 weaverryan

    You're totally right - you would do exactly this for i18n :)

  • 2016-06-15 JLChafardet

    such endless posibilities of usability with this kind of validation....
    i18n should be usable with the minMessage="error_message_min_message_whatever", I'll check on the translation services later lol
    I'm loving this.