Configurando validator com annotation e cache no silex

Por Jean Hertel, 10/03/2017

symfony , silex , validator , cache , php

Se você leu o nosso artigo sobre como configurar o Symfony Serializer com annotation e cache você já deve saber o básico da configuração necessária. Assim como o Serializer, o Validator também pode ler annotations e guardar os metadados da classe em cache.

Para ler as annotations, tudo que você precisa fazer é reescrever a função validator.mapping.class_metadata_factory. Esta função deve retornar uma instancia do objeto Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface.

Se você quiser fazer isso, pode usar o seguinte snippet de código:

<?php
use Silex\Provider\ValidatorServiceProvider;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
use Doctrine\Common\Annotations\AnnotationReader;

$app->register(new ValidatorServiceProvider());

$app['validator.mapping.class_metadata_factory'] = function ($app) {
    $loader = new AnnotationLoader(new AnnotationReader());

    return new LazyLoadingMetadataFactory($loader);
};

Desta forma o seu validador vai ler automáticamente as annotations que estiverem em sua classe. Por fim, assim como no caso do serializer, para guardar os dados em cache você pode passar o segundo argumento da classe LazyLoadingMetadataFactory.

A diferença a ser notada aqui, é que na versão atual do Symfony/Validator (3.2) a classe espera receber um parâmetro do tipo Symfony\Component\Validator\Mapping\Cache\CacheInterface. Felizmente, se você estiver usando o cache do Doctrine, já existe uma classe wrapper que pode ser usada, seu nome é DoctrineCache.

A seguir um exemplo completo e funcional de toda a implementação:

<?php
use Doctrine\Common\Annotations\AnnotationRegistry;
use Silex\Application;
use Silex\Provider\ValidatorServiceProvider;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory;
use Symfony\Component\Validator\Mapping\Cache\DoctrineCache;

use \Memcache as MemcachePHP;
use Doctrine\Common\Cache\MemcacheCache as MemcacheWrapper;

use Symfony\Component\Validator\Constraints as Assert;

class Foo
{
    /**
        * @var int
        * @Assert\NotBlank(message="This field cannot be empty")
        */
    private $someProperty;

    public function getSomeProperty() {
        return $this->someProperty;
    }

    public function setSomeProperty($someProperty) {
        $this->someProperty = $someProperty;

        return $this;
    }
}

$loader = require __DIR__ . '/vendor/autoload.php';
AnnotationRegistry::registerLoader([$loader, 'loadClass']);

$memcache = new MemcachePHP();

if (! $memcache->connect('localhost', '11211')) {
    throw new \Exception('Unable to connect to memcache server');
}

$cacheDriver = new MemcacheWrapper();
$cacheDriver->setMemcache($memcache);


$app = new Application();

$app->register(new ValidatorServiceProvider());

$app['validator.mapping.class_metadata_factory'] = function ($app) use ($cacheDriver) {
    $loader = new AnnotationLoader(new AnnotationReader());

    $cache = new DoctrineCache($cacheDriver);

    return new LazyLoadingMetadataFactory($loader, $cache);
};

$app->get('/', function (Application $app) {
    $obj = new Foo();

    $validationIssues = $app['validator']->validate($obj);

    $errors = (string)$validationIssues;

    return new \Symfony\Component\HttpFoundation\Response("Issues found: " . $errors);
});

$app->run();

Observação: Caso você esteja com algum erro de classe não encontrada, estou usando as seguintes depêndencias no meu composer.json: