Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Registration Validation

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

Registration Validation

Let’s add some validation. What if I don’t enter a valid email address or I choose a username that’s already taken? Right now, the form would submit just fine, which is lame.

HTML5 Validation

Try to submit a blank form right now! Woh! We do have some validation. In fact, if I enter an invalid email address, we see another error. This is HTML5 validation. When I inspect a field, we see what’s triggering it:

<input type="email" id="user_register_email" name="user_register[email]" required="required" />

First, each field has a required attribute, which tells an HTML5-compliant browser to throw an error if the field is left blank. Second, the input type=email field tells the browser to expect a valid email address instead of any string.

We got an input type=email field because we’re using the email field type in our form. But where are these required attributes coming from?

Field Options

To answer that, look at the third argument when adding a field: the options array:

// src/Yoda/UserBundle/Form/RegisterFormType.php
// ...

$builder->add('email', 'email', array(
    // an array of options to pass to this field

Every field type can be configured in different ways. For example, the repeated field has a type option. There are also a bunch of options that every field has, required being one of them. Set the required option on email to false and refresh:

// src/Yoda/UserBundle/Form/RegisterFormType.php
// ...

    // ...
    ->add('email', 'email', array(
        'required' => false
    // ...

When we inspect, we see that the required attribute is gone. All fields have this option, and it’s important to realize that it defaults to true.

There are a lot more options available for each field type. The easiest way to learn about them is via the documentation. Remember that Form Field Type Reference page we saw earlier? Yep, it shows you all the field types and all of their options.

Digging into the Core

You can also dig into the source code to find out what options are available. For the repeated type, there is a class called, well, RepeatedType. The setDefaultOptions method shows you the options that are special to this type.

Most of the global options are inherited from a class called FormType. In here you can see required and a few others. In its parent class, BaseType, we see a few more, like label and attr. We can use these to customize the label or add a class to the field from right inside the form class:

// src/Yoda/UserBundle/Form/RegisterFormType.php
// ...

    ->add('email', 'email', array(
        'required' => false,
        'label' => 'Email Address',
        'attr'    => array('class' => 'C-3PO')
    // ...

When we refresh, we see these options working for us.

Disabling HTML5 Validation

HTML5 validation is nice, but not enough: we still need server-side validation. Also, you can’t really customize how HTML5 validation looks or its messages easily. And finally, Symfony automatically defaults all fields to have the required attribute, which is kind of annoying.

I recommend avoiding HTML5 validation entirely. To disable it, just add a novalidate attribute to your form tag:

{# src/Yoda/UserBundle/Resources/views/Register/register.html.twig #}

{# ... #}
<form action="{{ path('user_register') }}" method="POST" novalidate="novalidate">

Refresh the form and try to submit empty. We get a huge error from the database which proves that HTML5 validation is off! Now let’s add some server-side validation!

Leave a comment!

Login or Register to join the conversation
Default user avatar
Default user avatar Michael Sypes | posted 5 years ago

Small error noted at 0:37. The narration and text script are correct in pointing out the required attribute as the key component in HTML5 validation, but the video incorrectly highlights the required class in the label, which is irrelevant.

1 Reply

Thumbs up - totally right, we should have highlighted the attribute, not the class (and not on the label).

Thanks for pointing that out!

Default user avatar

How do i insert a complete class like the RepeatedType class in the video example?


Hi Oscar!

I don't quite understand your question - what do you mean by "insert a complete class"? Do you want to add your own custom form field? Or are you interested in how to work with embedded forms (where you have one form type embedded inside another)?




I have a question, is there a way to make error fields global to the form without losing the markup of the invalid field ? Because when using "error_bubbling" option, makes the invalid field to lose its specific error markup

Thanks in advance.


Hey again Diego!

Hmm. In short, it shouldn't lose its markup. Obviously, when you bubble an error, it will then show at the *top* of your form, when you call form_errors(form). However, the same markup should be used to render *those* errors as is used to render the errors for an individual field inside form_row(form.someField). However, if you form error styling relies on being inside some div that's rendered by "form_row", then you obviously won't have that markup when the fields are rendered globally.

In other words, this shouldn't happen. So, tell me more :)


Hi Ryan!

What I'm doing is bubbling a field's error, then in my template I do:
to render all global errors, and indeed the bubbled field appears in there, then, I render the rows like this:
but the "bubbled_field" is been rendered as a valid one.

I did a dump on "form.bubbledField.vars" to see whats happening
When the errors is not bubbled the "vars.valid" attribute is set to false (if the field was submitted wrong of course)
but, if the error is bubbled, the valid attribute is set to true no matter what.
and thats why the bubbled field is losing its markup when been rendered by form_row(form.bubbledField), it can't tell if the field was valid or not.

Thanks for your time ;]


Hi Diego!

Ah, yes - I understand well now! If you bubble that error, then indeed it is attached at the global level. The "vars.valid" is actual set simply by counting to see if there are more than one errors attached to *that* field. So, this is expected behavior: you have attached the error to a different field (really, the global form).

What are you ultimately trying to accomplish?



Hey there!

I really appreciate your interest, cheers for that!

I'm working on a registration form, where the user must accept the legal terms via checkbox, so if it's not checked when submitting it must show an error.
For this case I want that error to appear at the global level of the form, but I still want to render the checkbox_row as an invalid field because must of the time users have tunnel vision and ignores the error messages at the top, so that's why I find really important to keep it's markup


Ah, in this case, I would probably:

A) Not bubble the field
B) Render the checkbox field like normal, but override the errors variable when you call form_row - set it to an empty array - {} - in Twig. I've never tried this before, but it should allow for valid to be true, but the errors won't actually print down there.
C) Render form_errors(form.agreeTerms) at the top of your form manually

There's no automatic way to get what you want, but if you follow this steps, it should be easy to do it manually :).


Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "~2.4", // v2.4.2
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.0.1
        "symfony/assetic-bundle": "~2.3", // v2.3.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.5
        "symfony/monolog-bundle": "~2.4", // v2.5.0
        "sensio/distribution-bundle": "~2.3", // v2.3.4
        "sensio/framework-extra-bundle": "~3.0", // v3.0.0
        "sensio/generator-bundle": "~2.3", // v2.3.4
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
        "ircmaxell/password-compat": "~1.0.3", // 1.0.3
        "phpunit/phpunit": "~4.1" // 4.1.0