Simple Website with Silex and Twig: Adding Form Validation

/ PHP / by Paul Robinson / 4 Comments
This post was published back on December 8, 2014 and may be outdated. Please use caution when following older tutorials or using older code. After reading be sure to check for newer procedures or updates to code.

In that last part of our tutorial we looked at how to add Swiftmailer to the contact form of our simple Silex website. This time we are going to look at how to finish things off by using the Form Builder, adding CSRF protection and moving our Silex install to a more secure location. If you haven’t followed the first two parts of this tutorial, head over to part 1 and part 2 now. We’ll wait.

I know some like to grab the source and follow through the tutorial, so here is an updated source pack. The demo link points to the same demo as the other two tutorials, just updated to include the new changes. The source pack has not been changed to implement moving of the Silex source. That is dependant on your folder setup and so is something you must do.


If you are using the source pack with composer you can run a quick composer update if you want to make sure everything is up-to-date.

Adding CSRF protection and the Form Builder are going to both happen at the same time. This is because adding the Form Builder also automatically adds CSRF protection to your form. So let’s start by adding the dependencies needed by Silex. Remember you only need to follow this section if you have used Composer. If you downloaded the Fat archive of Silex you should already have these included.

Composer Dependencies

There are a few new dependencies. Here they are:

So your full composer.json file should look like this.

You can, of course, just install them by running composer require but it’s quicker just to edit your composer.json file.

Do a composer update and you should be good to go.

Registering Services

Now that we have our dependencies we can register them. First up let’s update our namespace uses. You should currently have:

Just after your require for the Silex autoload file. Now let’s add our Form Service and the Validator constraints.

The order doesn’t matter, that’s just the order they are in my file. The FormServiceProvider is used to provide the Form builder, it also automatically includes the CSRF protection. Validator\Constraints is a selection of validation constraint methods such as NotBlank() to make sure a field is not empty, or Email() to make sure a value is a valid email address. These will all be accessible via the Assert namespace.

Now under all your other app registrations, your last one will probably be for SwiftMailer:

Add in these to register our Validator, Translation and Form services.

Although we won’t be handling translations in this tutorial you must include it. If you don’t the world will implode… No really, your application will error if you don’t include it so please don’t remove it even if you don’t plan to include translations with your app.

Using The Form Builder

Now that we have registered our now services with our app we can move onto using them. First up we need to make a small modification to our /contact route. Due to how the Silex form service works we now only need one contact route as the same one will handle outputting the contact page & dealing with the form POST data. So let’s sort that. Find your route definition:

We only need a very slight change, but it is very important:

This route will now match any HTTP verb provided it matches the /contact path. We also pass in our $request data for your form to use.

Now let’s get down to using the Form Builder. Our route will now look a little different so I’m going to go through it piece by piece. Here we go.

First up we create our route, recapping just in case, and set the sent variable to false by default.

We setup a defaults array for our Form Builder to use. This pre-fills the form with these details. Great for a edit form in a CRUD app where fields will already have values. Feel free to leave these blank since a contact form generally does not have pre-filled fields. Extra Warning: Remember these are not placeholders. They will not disappear when the field is typed in, they will be real text values.

This is the actual form building. While I know it may look confusing at first, if you read it through carefully it actually is fairly self-explanatory. Let’s just look at the name field as an example.

We add a form field called name. We make a text field, and then we pass an array as the third parameter. This defines the validation constraints. In this case, it cannot be blank (required) or less than 3 characters in length. We also pass along a class (for Bootstrap) and a placeholder as the attr array, you would pass any attribute you might add to the input tag in here such as ID.

The field’s label will automatically be a capitalized version of the field name, but you can also add the label to the outer array of the third parameter if you would like to customize that to be something else.

There is a list of the available field types, there is also a list of the available Validation Constraints. All Validation Constraints listed would be accessed (here at least) via Assert\{ValidationConstraint()} where {ValidationConstraint()} is the constraint from the linked list.

Finally we handle the request data. This passes the POST data to the Form Builder and allows us to check it against the Validation Constraints.

This checks to see if the data we have given to the form from the $response variable passes validation. If it does we grab the processed data back from the Form Builder and then use it to send our mail via SwiftMailer as we did in the previous tutorial. We also set our $sent variable to true to inform our view to show our ‘mail sent’ message.

Now that we’ve rebuilt our /contact route you can delete the old POST route we used in the last part, if you haven’t already done so.

Amending The View

Now that we’ve changed our route we will need to alter our contact.twig view to use the Form Builder. To do this find your form. Remove the whole form and replace it with:

This will render out the form. Now if you refresh you will have a fairly good looking form as Bootstrap should be styling it thanks to those extra form-control classes we added. However the spacing is not quite correct. This is because while Silex does wrap each form control in a div it doesn’t add the form-group class needed by Bootstrap. Let’s add that in.

To add that class in we will need to override the default template used by the Form Builder. Specifically the form_row block. To do that first create a new folder in your views folder called form, then inside there make a file called fields.twig. In this file add the following code:

This will add the form-group class and also add in the error classes to make the fields show the bootstrap error state if there are any errors for that specific field. Now once you’ve saved that head back to your contact.twig file and just after the line that extends the default layout (generally line 1) add the following:

This will override the default form_row block with our bootstrap compatible one. It technically isn’t needed to have a is defined here as the form will always be sent to the template, however it is good practice to do it anyway as running form_theme when the form variable is undefined will cause an error.

Moving Silex

We are finally done. All that’s left now is to add a little extra bit of security by moving our Silex install to outside of the web accessible root. To do this move the vendor folder and your composer files (composer.json and composer.lock) and place them one folder up from from your web accessible root, or you can place them one up and in a folder. Let’s say you are on a *nix server and have the following (generally default for some) folder structure:

You might want to do the following instead:

Then in your index.php file change the path to your vender/autoload.php file. In this case the code might be:

Also remember that you would need to run any composer commands in your silex folder, not your www folder as you would have been. I’ve read some debate about whether to move the views folder, but I believe they should be fine as without Twig to load them they are just as safe to be accessible as HTML files.

All Done!

That’s it, we have finally finished. I hope you have enjoyed this trio of tutorials, if you have any problems, questions, or you’ve improved something, please get in touch in the comments below. I try to read them all as soon as I get them.

P.S. Oh, and you may be wondering why I haven’t mentioned fixing the contact link not being marked active? This is now automatically fixed due to using the same route for both the GET and POST HTTP verbs.


Author’s gravatar

Thank you for pointing that out. Your post really makes sense to my research. I also found this article helpful and related to the issue but your ideas enlightened me the most. I am using this entire code but the only problem is that the form is being emailed on refreshing/reloading the page. What’s wrong with it?

Author’s gravatar author

Hi Jasper,

Do you mean that even if you don’t submit the form, the form is being send on refresh or page load? If that is the case all I can suggest is to make sure the exactly the same. Unless of course you are using the source pack.

If you are using the source pack (or your code is error free) & still having the same issue, please do let me know & I’ll investigate the issue to make sure there are no errors I’ve missed in the code.

Author’s gravatar

Nice article, thank you! Just so that you know, the links to the part1 and part2 are not correct (the correct ones don’t have ‘2014/10/’ in them…’) and lead to 404 pages. In case you have a second to correct that… 🙂

Author’s gravatar author

Hi IZ,

Glad it was helpful.

Thanks for letting me know, I’ve just corrected them. 🙂

Older Comments
Newer Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I'll keep your WordPress site up-to-date and working to its best.

Find out more