TSantos Serializer Bundle¶
This bundle integrates the library TSantos Serializer into a Symfony application.
Installation¶
Applications that use Symfony Flex¶
Open a command console, enter your project directory and execute:
$ composer require tsantos/serializer-bundle
Applications that don’t use Symfony Flex¶
Step 1: Download the Bundle¶
Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:
$ composer require tsantos/serializer-bundle
This command requires you to have Composer installed globally, as explained in the `installation chapter`_ of the Composer documentation.
Step 2: Enable the Bundle¶
Then, enable the bundle by adding it to the list of registered bundles
in the app/AppKernel.php
file of your project:
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new TSantos\SerializerBundle\TSantosSerializerBundle(),
);
// ...
}
// ...
}
Usage¶
This bundle exposes the service tsantos_serializer
which contains a
reference to :class:TSantos\Serializer\SerializerInterface
:
$serializer = $container->get('tsantos_serializer');
$serializer = $serializer->serialize($post);
$serializer = $serializer->deserialize($post, Post::class);
Note
The format used by the serializer is the one configured in your configuration file. Please go to the Configuration Reference page to see more information about this configuration.
Note
You can create and register new formats. Please, read the dedicated documentation about encoders at Encoder / Decoder page.
Auto-wiring the Serializer¶
Instead of fetching the serializer directly from the container, you can define the serializer as a dependency:
// src/Controller/DefaultController.php
use TSantos\Serializer\SerializerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
class DefaultController
{
private $serializer;
public function __construct(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}
public function indexAction(): JsonResponse
{
$post = ...;
return JsonResponse::fromJsonString($this->serializer->serialize($post));
}
}
Twig¶
This bundle ships with a simple Twig Filter that allows you serialize objects directly from twig template:
{# templates/index/posts.json.twig #}
{{ posts|serialize }}
Console Command¶
This bundle provides a console command useful for generate class metadata and serializer classes manually.
$ bin/console serializer:generate-classes
Note
You can see the list of generated classes by adding the -v option on the command above.
Tip
If your application is configured to never generate serializer classes automatically (best for production), add this command to your continuous deploy tool to generate the classes before install your application in production.
Configuration Reference¶
Default Configuration¶
tsantos_serializer:
debug: "%kernel.debug%"
format: "json"
class_path: "%kernel.cache_dir%/tsantos_serializer/classes"
generate_strategy: "file_not_exists"
mapping:
auto_configure: true
paths: { }
cache:
type: file
prefix: TSantosSerializer
path: "%kernel.cache_dir%/tsantos_serializer/metadata"
Configuration Reference¶
- debug
- Enable or disable some production optimizations.
- format
- The input and output format of serialization operations (e.g: encoder)
- class_path
- Directory that will store the generated classes
- generate_strategy
Class generation strategy. Can be one of:
never: serializer classes will never be generated (best for production environment, but will require to generate the classes in your continous deployment system).
always: every time a new class will be generated (best for debugging).
file_not_found: serializer classes will be generated only if the class not exists (best for development environment)
- mapping.auto_configure
The bundle will try to read the mappings automatically from the directories following the order bellow:
- config/serializer - YAML or XML configuration files
- src/Entity - Annotations
- src/Document - Annotations
- src/Model - Annotations
- mapping.paths
A list of paths and its namespaces like so:
tsantos_serializer: mapping: paths: - { namespace: "My\\Document", path: "%kernel.project_dir%/config/serializer" }- mapping.cache.type
- Type of cache implementation. Should be one of file, psr or doctrine.
- mapping.cache.prefix
- Prefix of cache keys. Required only for doctrine and psr cache types.
- mapping.cache.path
- Directory that will stored the metadata cache files. Required only for file cache type.
Event Dispatcher¶
Sometimes you need to change the state of the data before and/or after some serialization operation. In TSantos Serializer you can accomplish this through Event Listeners and Event Subscribers.
Event Listeners¶
Event listeners are single PHP methods or callable that will be called when some event is triggered by the serializer. To register listener in this bundle, first you need to create a class:
// src/EventListener/PostSerializerListener.php
namespace App\EventListener;
use TSantos\Serializer\EventDispatcher\Event\PreSerializationEvent;
use TSantos\Serializer\EventDispatcher\EventSubscriberInterface;
use TSantos\Serializer\EventDispatcher\SerializerEvents;
class PostSerializerListener
{
public function onPreSerialization(PreSerializationEvent $event): void
{
$post = $event->getData();
$post->setTitle('serialized_title');
}
}
Then register and tag it with tsantos_serializer.event_listener:
services:
App\EventListener\PostSerializerListener:
tags:
- { name: "tsantos_serializer.event_listener", event:"serializer.pre_serialization", method:"onPreSerialization" }
You can even omit the attribute method from the tag if your class is invokable by implement the __invoke method:
// src/EventListener/PostSerializerListener.php
namespace App\EventListener;
use TSantos\Serializer\EventDispatcher\Event\PreSerializationEvent;
use TSantos\Serializer\EventDispatcher\EventSubscriberInterface;
use TSantos\Serializer\EventDispatcher\SerializerEvents;
class PostSerializerListener
{
public function __invoke(PreSerializationEvent $event): void
{
$post = $event->getData();
$post->setTitle('serialized_title');
}
}
services:
App\EventListener\PostSerializerListener:
tags:
- { name: "tsantos_serializer.event_listener", event:"serializer.pre_serialization" }
Event Subscribers¶
A better way to define event listener is through event subscribers. All the above examples can be achieved by creating a subscriber class:
// src/EventListener/PostSerializerListener.php
namespace App\EventListener;
use TSantos\Serializer\EventDispatcher\Event\PreSerializationEvent;
use TSantos\Serializer\EventDispatcher\EventSubscriberInterface;
use TSantos\Serializer\EventDispatcher\SerializerEvents;
class PostSerializerListener implements EventDispatcherInterface
{
public static function getListeners(): array
{
return [
SerializerEvents::PRE_SERIALIZATION => 'onPreSerialization',
];
}
public function onPreSerialization(PreSerializationEvent $event): void
{
$post = $event->getData();
$post->setTitle('serialized_title');
}
}
Thanks to Symfony DIC`s auto-configuration mechanism, all you need to do is to create your subscriber class and make sure that it implements the :class:`EventDispatcher\\EventDispatcherInterface` interface.
Note
If you are using a Symfony version prior to 3.3, you’ll need to register and tag manually the service.
services:
App\EventListener\PostSerializerListener:
tags:
- { name: "tsantos_serializer.event_subscriber"}
Normalizer / Denormalizer¶
(De)normalizers are powerful services that transforms the data without encoding/decoding them. For example, supposing your entity has a date/time field. In this case, you don’t need to create a custom mapping for :phpclass:`\DateTime` class to define how to configure such data type. Instead, all you need is to create your custom (de)normalizers:
// src/Serializer/DateTimeNormalizer.php
namespace App\Serializer;
use TSantos\Serializer\Normalizer\DenormalizerInterface;
use TSantos\Serializer\Normalizer\NormalizerInterface;
use TSantos\Serializer\DeserializationContext;
use TSantos\Serializer\SerializationContext;
class DateTimeNormalizer implements NormalizerInterface, DenormalizerInterface
{
private $format;
public function __construct(string $format = \DateTime::ATOM)
{
$this->format = $format;
}
public function normalize($data, SerializationContext $context)
{
if (!$data instanceof \DateTimeInterface) {
throw new InvalidArgumentException('Data should be instance of ' . \DateTimeInterface::class);
}
return $data->format($this->format);
}
public function supportsNormalization($data, SerializationContext $context): bool
{
return $data instanceof \DateTimeInterface;
}
public function denormalize($data, DeserializationContext $context)
{
return \DateTime::createFromFormat($this->format, $data);
}
public function supportsDenormalization(string $type, $data, DeserializationContext $context): bool
{
return $type === \DateTime::class || $type === \DateTimeInterface::class;
}
}
This bundle automatically recognize services that implements :class:`Normalizer\\NormalizerInterface` and :class:`Normalizer\\DenormalizeInterface` and tag them with the proper tag name.
See also
Please, refer to TSantos Serializer Library repository for a detailed documentation about the normalization process and the built-in normalizers.
Note
If you are using a Symfony version prior to 3.3, you’ll need to register and tag the normalizer services manually.
services:
App\Serializer\UserNormalizer:
tags:
- { name: "tsantos_serializer.normalizer"}
- { name: "tsantos_serializer.denormalizer"}
Tip
You can use normalizers to transform read-only entities and avoid unnecessary over-head due the serialization process:
// src/Serializer/UserNormalizer.php
namespace App\Serializer;
use App\Entity\User;
use App\Repository\UserRepository;
use TSantos\Serializer\Normalizer\DenormalizerInterface;
use TSantos\Serializer\Normalizer\NormalizerInterface;
class UserNormalizer implements NormalizerInterface, DenormalizerInterface
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function normalize($data, SerializationContext $context)
{
return $data->getId();
}
public function supportsNormalization($data, SerializationContext $context): bool
{
return $data instanceof User;
}
public function denormalize($data, DeserializationContext $context)
{
return $this->userRepository->find($data);
}
public function supportsDenormalization(string $type, $data, DeserializationContext $context): bool
{
return $type === User::class;
}
}
Encoder / Decoder¶
Encoders are services that transform data from string to array and vice-versa. Although the TSantos Serializer Library currently supports only JSON encoder, you can register new encoders to your application by implementing the :class:`Encoder\\EncoderInterface` interface, register the service in the container and tag it like following:
// src/Serializer/JsonEncoder
namespace App\Serializer;
use TSantos\Serializer\Encoder\EncoderInterface;
class JsonEncoder implements EncoderInterface
{
public function encode(array $data): string
{
return json_encode($data);
}
public function decode(string $content): array
{
return json_decode($content, true);
}
public function getFormat(): string
{
return 'json';
}
}
Now you can tag your encoder and you are done to use your custom encoder.
# ./config/services.yml
services:
App\Serializer\JsonEncoder:
tags:
- { name: "tsantos_serializer.encoder", format: "format" }
Note
The attribute format is required! This value will be matched against the option tsantos_serializer.format on your configuration to define which encoder will be used in your application.
Note
Different from the most used serialization libraries, you don’t need to pass the format on $serializer->serialize(…) or $serializer->deserialize(…) calls.