Configuring serializer with annotation and cache in silex

By Jean Hertel, 07/02/2017, in category Tips

Cache, Serializer, Silex, Symfony

Hello readers,

Today I needed to turn some objects into json to return through a web request with the Silex framework. As I suffered enough to configure and figure out how to cache properly, I decided to write this post in the hope of helping others who venture down this path.

The first step of all is to have a Silex application running and functional. After doing this, you can download the library and begin serializing the objects.

There are two very good libraries for object serialization in general. The first one is JMSSerializer, which is the most complete and has all sorts of annotation. The second library is the Symfony serialization component . For both cases there are available providers, but the Symfony serializer is already preconfigured in Silex, just add the dependency. For details on how to configure the provider see this link.

After you install the dependency, you need to configure it. In the site of silex the suggestion is:

<?php
$app->register(new Silex\Provider\SerializerServiceProvider());

With this code the service $app['serializer'] becomes available. I will not stick to the use of the serializer, but only to the detail that it supports annotations.

To configure annotations, you need to add an annotation reader to the class that will extract the metadata from the object. Usually you will want to extract the metadata through the getters and setters or through the properties of the class. For this purpose you can use respectively for get/set and for properties The Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer reader and `Symfony\Component\Serializer\Normalizer\PropertyNormalizer.

For each of these classes it is possible to pass an instance of Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface. It is this instance that we can manipulate to read annotations:

<?php
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use \Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use \Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use \Doctrine\Common\Annotations\AnnotationReader;

$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$meuSerializerNinja = new GetSetMethodNormalizer($classMetadataFactory);

Now that we have the serializer we need to register it to the container:

<?php
$app['serializer.normalizers'] = function () use ($meuSerializerNinja) {
    return [$meuSerializerNinja];
};

Finally, if you want to add cache to the annotation reader, you can do this by passing an instance of Doctrine\Common\Cache\Cache as the second parameter of the constructor from ClassMetadataFactory.

Fully functional sample:

<?php
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;

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

    /**
     * @return int
     * @Groups({"some_group"})
     */
    public function getSomeProperty() {
        return $this->someProperty;
    }
}

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

$loader = require_once __DIR__ . '/../vendor/autoload.php';

\Doctrine\Common\Annotations\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 \Silex\Application();

$app->register(new Silex\Provider\SerializerServiceProvider());

$app['serializer.normalizers'] = function () use ($app, $cacheDriver) {
    $classMetadataFactory = new Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory(
        new Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader(new AnnotationReader()), $cacheDriver);

    return [new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer($classMetadataFactory) ];
};

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

    $json = $app['serializer']->serialize($foo, 'json');

    return new \Symfony\Component\HttpFoundation\JsonResponse($json, \Symfony\Component\HttpFoundation\Response::HTTP_OK, [], true);
});


$app->run();

I hope it has helped you. Hugs.