Ebooks

Symfony validation tutorial

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 heavily inspired by 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 the the newly created project directory.

$ composer require annotations
$ composer require maker
$ composer require twig

We install three basic Symfony packages: annotations, maker, and twig.

$ composer require symfony/validator

The symfony/validator package contains the Symfony validation tools.

$ composer require symfony/security-csrf
$ composer require symfony/monolog-bundle

The security-csrf package is needed against cross-site request forgeries and monolog-bundle for logging.

Building Symfony application

$ php bin/console make:controller HomeController

We create a HomeController. The controller sends a form to the client.

src/Controller/HomeController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;

class HomeController extends AbstractController
{
    /**
        * @Route("/", name="home")
        */
    public function index()
    {
        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 submition.

src/Controller/FormController.php
<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validation;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Psr\Log\LoggerInterface;

class FormController extends AbstractController
{
    /**
     * @Route("/sendForm", name="form")
     */
    public function index(Request $request, ValidatorInterface $validator, 
            LoggerInterface $logger)
    {
        $token = $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->get("name");
        $email = $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) {
            return $this->render('form/violations.html.twig', 
                    [ 'violations' => $violations ] );
        } 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.

public function index(Request $request, ValidatorInterface $validator, 
        LoggerInterface $logger)
{

We inject the request object, validator object, and logger object.

$token = $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->get("name");
$email = $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.

if (count($violations) > 0) {
    return $this->render('form/violations.html.twig', 
            [ 'violations' => $violations ] );
} else {
    return new Response("Validation passed",  Response::HTTP_OK, 
            ['content-type' => 'text/plain']);
}   

In there are some validation violations, we send a view with errors to the client. Otherwise, we send a plain message Violation passed.

templates/home/index.html.twig
{% 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.

templates/form/violations.html.twig
{% extends 'base.html.twig' %}

{% block title %}Home page{% endblock %}

{% block body %}

<h2>Validation failed</h2>

<ul>
{% for violation in violations %}
    <li>{{violation.getPropertyPath()}}: {{ violation.getMessage }}</li>
{% endfor %}
</ul>

{% endblock %}

In the violations view, we go through the violations and list them.

templates/base.html.twig
<!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.

In this tutorial we have validated a simple form in a Symfony application.

You might also be interested in the following related tutorials: Introduction to Symfony, Symfony DBAL tutorial, Symfony upload file tutorial, Symfony translation tutorial, Symfony service tutorial, Symfony form tutorial, PHP tutorial.