Skip to content

Instantly share code, notes, and snippets.

@felds
Created April 11, 2012 18:33
Show Gist options
  • Save felds/2361223 to your computer and use it in GitHub Desktop.
Save felds/2361223 to your computer and use it in GitHub Desktop.
Validadores personalizados no Symfony2

Validadores personalizados no Symfony2

Para criar um validador personalizado, devemos criar as seguintes classes:

  1. Constraint, responsável por configurar o comando
    Esta classe vai expor a configuração do comando, seja por Annotation, YAML, etc.
  2. Validator, que executa todo o processo de validação

Validando um campo

Como situação de teste, vamos verificar se o usuário está preenchendo o campo com o valor esfiha.

Para validar um campo, comecemos com a configuração do contraint:

<?php
// src/Acme/FoodBundle/Validator/NotEsfiha.php

namespace Acme\FoodBundle\Validator;

// importa a classe base
use Symfony\Component\Validator\Constraint;

/**
 * @Annotation
 */
class NotEsfiha extends Constraint
{
    // a única configuração exposta neste validador será a mensagem
    // para expor outras configuração, é só definir mais propriedades públicas como esta.
    public $message = 'acme_food.test.esfiha';
    
    public function validatedBy()
    {
        return get_class($this).'Validator';
    }
}

Esta configuração irá despachar o processo de validação para a classe retornada pelo método validatedBy.
Neste caso, a classe chamada será a NotEsfihaValidator.

<?php
// src/Acme/FoodBundle/Validator/NotEsfihaValidator.php

namespace Acme\FoodBundle\Validator;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class NotEsfihaValidator extends ConstraintValidator
{
    public function isValid($value, Constraint $constraint)
    {
        // o valor do campo em que este validador for aplicado será passado pelo
        // primeiro argumento ($value, neste caso).
        if ('esfiha' == strtolower($value)) {
            // caso a validação falhe, devemos configurar a mensagem de retorno.
            // para isso, pegaremos a mensagem no configurador (Constraint),
            // passado no segundo parâmetro ($constraint)
            $this->setMessage($constraint->message);

            return false;
        }

        return true;
    }
}

Pronto!
O validador já está pronto para ser usado em um campo.

<?php

use Acme\FoodBundle\Validator\NotEsfiha;

class Dish
{
    /**
     * note que eu estou omitindo o resto das configurações da
     * entidade para simplificar o exemplo
     * 
     * @NotEsfiha(message="Esfiha? Hummmm… hoje não.")
     */
    public $name;
}

Validando mais de um campo

Para validar mais de um campo ao mesmo tempo (comparar campos, por exemplo), é necessário apenas o "alvo" do Constraint (configurador) e a forma como a validação é feita no Validator, que agora receberá o objeto completo, ao invés de apenas uma propriedade.

Alterando o alvo (escopo) do Constraint de property para class:

<?php
// src/Acme/FoodBundle/Validator/NotEsfiha.php

class NotEsfiha extends Constraint
{
    // ...

    public function getTargets()
    {
        return Constraint::CLASS_CONSTRAINT;
    }
}

Ajustando a Annotation:

<?php

use Acme\FoodBundle\Validator\NotEsfiha;

/**
 * A anotação sai do campo e vem para a classe.
 *
 * @NotEsfiha(message="Esfiha azul? WTF!?!?")
 */
class Dish
{
    public $name;

    // colocando um novo campo para que a gente tenha o que comparar
    public $color;
}

E agora é só validar os campos:

<?php
// src/Acme/FoodBundle/Validator/NotEsfihaValidator.php

class NotEsfihaValidator extends ConstraintValidator
{
    // note que agora o validador recebe o valor sobre o qual ele foi "anotado";
    // o que antes era um valor (propriedade), agora é um objeto (class)
    public function isValid($entity, Constraint $constraint)
    {
        if (
            'esfiha' == strtolower($entity->name)
            && 'blue' == strtolower($entity->color)
        ) {
            $this->setMessage($constraint->message);

            return false;
        }

        return true;
    }
}

Mais informações?

RTFM!

Pode melhorar esse guia?

Fork me!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment