Symfony 2.4, el libro oficial

11.1. Los fundamentos de la validación

La mejor manera de entender la validación es verla en acción. Para empezar, supongamos que has creado un objeto PHP normal y corriente que vas a utilizar en tu aplicación:

// src/Acme/BlogBundle/Entity/Author.php
namespace Acme\BlogBundle\Entity;

class Author
{
    public $name;
}

Por el momento esto es simplemente una clase PHP que se utiliza para algún propósito dentro de tu aplicación. La finalidad de la validación es decir si los datos de un objeto son válidos o no. Para que esto funcione, debes configurar una lista de reglas (llamadas restricciones y en inglés, constraints) que el objeto debe cumplir para considerarse válido. Estas reglas se pueden especificar en diferentes formatos (YAML, XML, anotaciones o PHP).

Para garantizar por ejemplo que la propiedad $name no esté vacía, añade lo siguiente:

# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
    properties:
        name:
            - NotBlank: ~
// src/Acme/BlogBundle/Entity/Author.php

// ...
use Symfony\Component\Validator\Constraints as Assert;

class Autor
{
    /**
     * @Assert\NotBlank()
     */
    public $name;
}
<!-- src/Acme/BlogBundle/Resources/config/validation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">

    <class name="Acme\BlogBundle\Entity\Author">
        <property name="name">
            <constraint name="NotBlank" />
        </property>
    </class>
</constraint-mapping>
// src/Acme/BlogBundle/Entity/Author.php

// ...
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints\NotBlank;

class Autor
{
    public $name;

    public static function loadValidatorMetadata(ClassMetadata $metadata)
    {
        $metadata->addPropertyConstraint('name', new NotBlank());
    }
}

Truco Las propiedades protegidas (protected) y privadas (private) también se pueden validar, así como los métodos getXXX().

11.1.1.  Usando el servicio validator

A continuación, para validar realmente el objeto Author, debes utilizar el método validate() del servicio validator (que corresponde a la clase Symfony\\Component\\Validator\\Validator). El trabajo del validator es fácil: lee las restricciones (es decir, las reglas) de una clase y comprueba si los datos en el objeto satisfacen esas restricciones. Si la validación falla, devuelve una lista no vacía con los errores producidos. El siguiente código muestra un ejemplo sencillo de validación dentro de un controlador de Symfony:

// ...
use Symfony\Component\HttpFoundation\Response;
use Acme\BlogBundle\Entity\Author;

public function indexAction()
{
    $author = new Author();

    // ...

    $validator = $this->get('validator');
    $errors = $validator->validate($author);

    if (count($errors) > 0) {
        /*
         * Utiliza el método __toString de la variable $errors, que
         * es un objeto de tipo ConstraintViolationList. Así se obtiene
         * una cadena de texto lista para poder depurar los errores.
         */
        $errorsString = (string) $errors;

        return new Response($errorsString);
    }

    return new Response('The author is valid! Yes!');
}

Si la propiedad $name está vacía, verás el siguiente mensaje de error (los mensajes no los verás en inglés, sino traducidos al idioma que hayas configurado en Symfony2):

Acme\BlogBundle\Author.name:
    This value should not be blank

Si la propiedad name tiene cualquier valor, verás el otro mensaje indicando que todo es correcto.

Truco En la práctica, casi nunca trabajas con el servicio validator directamente y nunca tienes que preocuparte de mostrar los mensajes de error a mano. En realidad, tu aplicación sólo se preocupa de manejar los formularios y la validación se ejecuta automáticamente en segundo plano y de forma transparente para tí.

Si lo prefieres, también puedes pasar la colección de errores a una plantilla.

if (count($errors) > 0) {
    return $this->render('AcmeBlogBundle:Author:validate.html.twig', array(
        'errors' => $errors,
    ));
} else {
    // ...
}

Dentro de la plantilla, puedes mostrar la lista de errores tal y como lo necesites:

{# src/Acme/BlogBundle/Resources/views/Autor/validate.html.twig #}
<h3>The author has the following errors</h3>
<ul>
{% for error in errors %}
    <li>{{ error.message }}</li>
{% endfor %}
</ul>
<!-- src/Acme/BlogBundle/Resources/views/Autor/validar.html.php -->
<h3>The author has the following errors</h3>
<ul>
<?php foreach ($errors as $error): ?>
    <li><?php echo $error->getMessage() ?></li>
<?php endforeach; ?>
</ul>

Nota Cada error de validación se denomina constraint violation o "violación de la restricción" y está representado por un objeto de tipo Symfony\\Component\\Validator\\ConstraintViolation.

11.1.2. Validación y formularios

El servicio validator se puede utilizar para validar cualquier objeto. Sin embargo, casi siempre utilizas el servicio validator de forma indirecta al trabajar con los formularios.

La librería de formularios de Symfony utiliza internamente el servicio validator para validar el objeto que se está manipulando a través del formulario. Las violaciones de restricción en el objeto se convierten en objetos FieldError que se pueden mostrar fácilmente en el formulario. Dentro de un controlador Symfony, el flujo de trabajo típico durante el envío de un formulario es el siguiente:

// ...
use Acme\BlogBundle\Entity\Author;
use Acme\BlogBundle\Form\AuthorType;
use Symfony\Component\HttpFoundation\Request;

public function updateAction(Request $request)
{
    $author = new Author();
    $form = $this->createForm(new AuthorType(), $author);

    $form->handleRequest($request);

    if ($form->isValid()) {
        // validación superada, haz algo con el objeto $author

        return $this->redirect($this->generateUrl(...));
    }

    return $this->render('BlogBundle:Author:form.html.twig', array(
        'form' => $form->createView(),
    ));
}

Nota Este ejemplo utiliza un formulario de la clase AutorType cuyo código no se muestra. Para más información, consulta el capítulo de los formularios.