Symfony Does Validation

📁 symfony

Since the beginning of computing the single most error-prone and unreliable component of the system has been something that isn’t really part of the system at all. Talk to anyone who’s worked in tech support and they’ll tell you the most common problem when troubleshooting computer malfunctions: operator error.

Humans are fallible, especially so when trying to communicate their intentions to a piece of complicated technology. As well as this, some humans are malicious, and may wish to intentionally enter unexpected data into an application to cause it to function outside of normal operating conditions. As a result, it’s important that we validate and sanitise the data that users enter into our web applications, and Symfony can help us with this.

How does it work?

Symfony’s validation framework allows us to add constraints which limit the possible values that data moving through our model may have. These constraints are statements about variables in our Entity classes that must hold to be true, otherwise validation of the class will result in an error state. Some examples of the kinds of constraints we can impose include:

  • $name is not blank
  • $title is at least 3 characters long and at most 255 characters long
  • $modifiedAt is a date
  • $age is at least 18
  • and many more…

Symfony implements constraints using “assertions.” We apply one or more assertions to properties of our entity classes, and then after adding data to an instance of the class we can check whether or not the assertions hold. If not, we can alter the flow of control through our application to display an error message, forward the user to a different page or take some other course of action.

Defining constraints

We can define our constraints using a number of different methods.

  • In a YAML file (this is the default method).
  • Using PHP annotations.
  • In an XML file.
  • In a PHP file.

We can change the configuration method that is utilised by editing the file:

# app/config/config.yml framework: # eg: use PHP annotations validation: { enable_annotations: true }

As you can see we’ve selected annotations in the configuration file above. Personally this is my preferred option, and the one we’ll use for the rest of the tutorial.

We can now define our constraints by adding assertions to the different properties of our entity classes, as illustrated below:

Here we can see the “not blank” assertion, which will cause the validation to fail if the $title variable is blank (null or the empty string). We have access to a wide range of validators like this for different datatypes; a full list of the possible assertions can be found at the official documentation.

Now that we’ve added this constraint to the entity, we can check it using a validator.

We’ll now have a nice and simple array of all the validation errors based on the data set in $article and the defined constraints. But of course this is an overly simplistic example. We usually don’t interact with validators by making the call to $this->get(‘validator’) directly. Instead, we normally use the validators in the context of defining and using forms, which is what we’ll look at next.

Using Validators with Forms

Of course the most obvious use-case for validators is making sure that data entered by users via a form fits the constraints which we (at least should) have defined when we created our model classes. We can do this using the combination of a custom form class, an entity class and assertions. See the controller below:

The first thing to notice is that when we create the form, we use custom form class ArticleType() and pass in an instance of the Article entity:

$form = $this->createForm(new ArticleType(), $article)

Obviously when the form is first displayed we won’t have any data to validate, so for the first stage of the form-filling process, we don’t need to do anything else apart from forward the form object to the view for rendering (not shown in the code sample above).

When the form is posted back, however, the situation changes somewhat. After checking is this is a postback, we need to take all of the variables supplied in the post data and apply them to the form object so the fields inside the instance of the form are populated. We do this using a call to ->bindRequest, as you can see below:

$form->bindRequest($request)

Finally, it’s time to check that the bound values are “valid” based on the assertions we’ve defined in the entity. We do this using a call on the form:

if ($form->isValid()) { ... }

(Non-controversially) this method will return ‘true’ if all the assertions pass, and false otherwise.

All together now

Now that we’ve seen the general workflow for validating a form, here’s an example of a form class, an entity class, a view and a controller all working together to validate user input.

So let’s get started! This is the entity we’ll use, complete with assertions:

It’s important to note the uses statements at the top of the class – without these the assertions won’t work! Also note that we’re making some UniqueEntity assertions at the class level – these ensure that the same username and email address are not entered more than once.

A quick disclaimer: this implementation is incredibly insecure and only for the purposes of illustration. Passwords should never be stored in plaintext in the database! With that in mind, let’s continue…

Here’s the UserType class that is used to generate the form:

This is quite straight forward, and most of the heavy lifting can actually be done using the command line interface. If you’ve already created the entity class, you can get Symfony to generate the form using the following:

$ php app/console doctrine:generate:form MyCompanyBlogBundle:User

You will notice one important thing though: the ‘password’ field has a second parameter. This is because the field, being a string, will default to a standard ‘text’ type input field. We want to override this and make it a ‘password’ type input field so the entered text is not immediately visible.

Now for the fun part. Here’s our controller:

Let’s break it down. The indexAction() function is very basic: it just pulls back all of the users from the database (or ORM layer if we want to nitpick) and sends them to the view for rendering.

createAction() is where the magic happens. If you follow the comments, you can see that we create instances of the User entity and the UserType shown above and then use them to create a Form. Control then branches based on whether this is a postback or not. If this is not a postback, we simply send the empty form to the view for rendering. If this is a postback, we bind the request parameters to the form and then run the validation ( ->isValid() ). If validation fails, we simply pass the form to the view and let it render the errors. Otherwise we use the Doctrine EntityManager to persist the User object (with the bound values) to the database, then redirect.

Of course now we need to display everything. Here are our two view classes:

Wrap up

And that’s how to use Form validation in Symfony… Phew!

It may seem like a lot of code, but the workflow really does make sense, and once you understand the conventions it truly does cut your development time down significantly while providing some very powerful features.

Still hungry for more information? Head on over to the official documentation for validation.