Symfony validation
last modified July 5, 2020
Symfony validation tutorial shows how to validate data in a Symfony application. In this tutorial, we use manual validation. We do not use doctrine annotations.
Symfony
Symfony is a set of reusable PHP components and a PHP framework for web projects. Symfony was published as free software in 2005. The original author of Symfony is Fabien Potencier. Symfony was inspired by Ruby on Rails, Djanog, and the Spring Framework.
Symfony validation
Input from users must be validated. Symfony provides a Validator
component
to perform validation tasks. The component is based on Java's Bean Validation specification.
The Validator is designed to validate objects against constraints.
Constraints are assertions that a condition is true. Symfony has many built-in
constraints including NotBlank
, Email
, Length
,
and Isbn
. It is possible to create custom constraints as well.
Symfony validation example
In the example, we have a simple form with two fields: name
and email
.
After the form is submitted, we manually validate the fields with
Symfony's Validator
. In the example, we use Length
,
NotBlank
, and Email
constraints.
Installing packages
$ composer create-project symfony/skeleton myval $ cd myval
We create a new Symfony project and go to the project directory.
$ composer req maker server --dev
We install symfony/maker-bundle
and symfony/web-server-bundle
.
These are useful for development mode. Note that we are using aliases for the
packages.
$ composer req twig annotations
We install the twig-bundle
and the annotations. Annotations are
located in the sensio/framework-extra-bundle
.
$ composer req validator
The symfony/validator
package contains the Symfony validation tools.
$ composer req security-csrf $ composer req monolog $ composer req property-access
The symfony/security-csrf
package is needed against cross-site request
forgeries, symfony/monolog-bundle
for logging, and
symfony/property-access
for manipulating PHP properties.
Building Symfony application
$ php bin/console make:controller HomeController
We create a HomeController
.
<?php namespace App\Controller; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class HomeController extends AbstractController { /** * @Route("/home", name="home") */ public function index(): Response { return $this->render('home/index.html.twig'); } }
This is a simple controller that sends a view containing the web form to the user.
$ php bin/console make:controller FormController
We create a FormController
that responds to the form submission.
<?php namespace App\Controller; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Validator\ValidatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class FormController extends AbstractController { /** * @Route("/sendForm", name="form") */ public function index(Request $request, ValidatorInterface $validator, LoggerInterface $logger): Response { $token = $request->request->get("token"); if (!$this->isCsrfTokenValid('myform', $token)) { $logger->info("CSRF failure"); return new Response("Operation not allowed", Response::HTTP_OK, ['content-type' => 'text/plain']); } $name = $request->request->get("name"); $email = $request->request->get("email"); $input = ['name' => $name, 'email' => $email]; $constraints = new Assert\Collection([ 'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank], 'email' => [new Assert\Email(), new Assert\notBlank], ]); $violations = $validator->validate($input, $constraints); if (count($violations) > 0) { $accessor = PropertyAccess::createPropertyAccessor(); $errorMessages = []; foreach ($violations as $violation) { $accessor->setValue($errorMessages, $violation->getPropertyPath(), $violation->getMessage()); } return $this->render('form/violations.html.twig', ['errorMessages' => $errorMessages]); } else { return new Response("Validation passed", Response::HTTP_OK, ['content-type' => 'text/plain']); } } }
In the FormController
, we check the CSRF token, validate the form
input values, and send a response back to the client.
Note: For simplicity reasons, we have placed the validation code into the controller. In a production application it is better to place such code in a separate service class.
public function index(Request $request, ValidatorInterface $validator, LoggerInterface $logger) {
We inject the request object, validator object, and logger object.
$token = $request->request->get("token"); if (!$this->isCsrfTokenValid('myform', $token)) { $logger->info("CSRF failure"); return new Response("Operation not allowed", Response::HTTP_OK, ['content-type' => 'text/plain']); }
We retrieve the token and validate it with isCsrfTokenValid()
method.
$name = $request->request->get("name"); $email = $request->request->get("email"); $input = ['name' => $name, 'email' => $email];
We get the request input parameters and place them into an array.
$constraints = new Assert\Collection([ 'name' => [new Assert\Length(['min' => 2]), new Assert\NotBlank], 'email' => [new Assert\Email(), new Assert\notBlank] ]);
We create a collection of constraints. We can assign multiple constraints for a single value.
$violations = $validator->validate($input, $constraints);
We validate the input data against the constraints with the validate()
method. The method returns possible violations. Symfony validator returns a
ConstraintViolationList
. We use the Symfony accessor to process
the list.
if (count($violations) > 0) {
We check if there are some violations.
$accessor = PropertyAccess::createPropertyAccessor(); $errorMessages = []; foreach ($violations as $violation) { $accessor->setValue($errorMessages, $violation->getPropertyPath(), $violation->getMessage()); }
Symfony PropertyAccess is used to process the violation messages, before
sending them to the template. The ConstraintViolationList
is transformed into a PHP array, where keys are form fields and values
are error messages. The array is later processed in Twig using the for
directive.
return $this->render('form/violations.html.twig', ['errorMessages' => $errorMessages]);
We render the error messages in a separate page. This is often done using flash messages. Have a look at Symfony keep form values tutorial how to do it with flashes.
} else { return new Response("Validation passed", Response::HTTP_OK, ['content-type' => 'text/plain']); }
If everything is OK, we send a plain message Validation passed.
{% extends 'base.html.twig' %} {% block title %}Home page{% endblock %} {% block body %} <form action="sendForm" method="post"> <input type="hidden" name="token" value="{{ csrf_token('myform') }}" /> <div> <label>Name:</label> <input type="text" name="name"> </div> <div> <label>Email</label> <input type="email" name="email"> </div> <button type="submit">Send</button> </form> {% endblock %}
The form contains two fields: name and email.
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />
It also contains a hidden field to guard against cross-site request forgeries.
{% extends 'base.html.twig' %} {% block title %}Violations{% endblock %} {% block body %} <h2>Validation failed</h2> <ul> {% for field, errorMessage in errorMessages %} <li>{{ field }}: {{ errorMessage }}</li> {% endfor %} </ul> {% endblock %}
In the violations view, we go through the violations and list them.
{% for field, errorMessage in errorMessages %} <li>{{ field }}: {{ errorMessage }}</li> {% endfor %}
With the for
directive, we go through error messages
and show them if there are any.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>{% block title %}Welcome!{% endblock %}</title> {% block stylesheets %}{% endblock %} </head> <body> {% block body %}{% endblock %} {% block javascripts %}{% endblock %} </body> </html>
This is the base Twig template.
$ php bin/console server:run
We start the development server. Then locate to the localhost:8000/home
url to get the form.
In this tutorial we have validated a simple form in a Symfony application. We have used manual validation.
List all Symfony tutorials.