Il existe de nombreuses possibilités de configuration et utilisation du composant Serializer de Symfony. Cet article a pour objectif de présenter la sérialisation ainsi que son utilisation.
La sérialisation consiste à transformer un objet en un format spécifique (comme le yaml, json, …) puis de pouvoir passer du format spécifique vers l’objet d’origine.
Les cas typiques d’utilisation de la sérialisation sont :
Le framework PHP Symfony propose un composant Serializer complet pour sérialiser les objets en différents formats simplement.
Schéma tiré de la documentation Symfony
Comme présenté dans ce schéma, la sérialisation normalise l’objet en un array avant de l’encoder dans un format spécifique. La désérialisation fait l’inverse, elle décode le format en un array avant de le dénormaliser en un objet.
Le composant Symfony peut s’installer simplement avec Composer :
composer require symfony/serializer
Mais le composant peut être à la place cloné via le repository github.
Le composant Serializer de Symfony inclut de nombreux normalizers et encoders.
Les 3 principaux normalizers
Les normalizers à utilisation plus spécifique
Nous avons la possibilité de créer un normalizer personnalisé en implémentant à notre class l‘interface NormalizerInterface. Le service doit avoir le tag serializer.normalizer (avec la configuration de service par défaut de Symfony le tag est automatique).
new_normalizer:
class: Path\to\class
public: false
tags: [serializer.normalizer]
Tout comme les normalizers, le composant Serializer inclut plusieurs encodeurs/decodeurs de format :
JsonEncoder, XmlEncoder, YamlEncoder et CsvEncoder.
Nous pouvons également créer des encoders et decoders pour des formats non gérés. Il faut que notre class implémente les EncoderInterface et DecoderInterface. Le service doit avoir le tag serializer.encoder, qui sera appliqué automatiquement avec la configuration par défaut de service de Symfony.
new_encoder_decoder:
class: Path\to\class
public: false
tags: [serializer.encoder]
En tant que composant indépendant
Dans le cas de l’utilisation du serializer indépendamment du framework Symfony, il faut :
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
//...
$encoders = array(new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$productSerialized = $serializer->serialize($product, 'json');
//...
En ce qui concerne la désérialisation, il faut appeler la méthode deserialize avec en paramètres les données sérialisées, la class et le format :
$productDeserialized = $serializer->deserialize($productSerialized, Product::class, 'json');
Il est possible de déserialiser les données directement dans un objet existant :
$productDeserialized>deserialize($productSerialized, Product::class, 'json', array('object_to_populate' => $product));
En tant que service Symfony
Dans le framework Symfony, on peut bien sûr créer notre serializer comme dans la partie précédente. Cependant, avec l’injection de service, on obtient directement une instance du serializer déjà configurée.
Par exemple, dans une action d’un Controller :
use Symfony\Component\Serializer\SerializerInterface;
//...
public function index(SerializerInterface $serializer)
{
$productSerialized = $serializer->serialize($this->getProduct(), 'json');
//...
Normalizer/Decoder
Dans ce cas, l’instance récupérer $serializer possède tous les encoders mais aussi des normalizers. Par ordre de priorité :
0 - JsonSerializableNormalizer
1 - DateTimeNormalizer
2 - ConstraintViolationListNormalizer
3 - DateIntervalNormalizer
4 - DataUriNormalizer
5 - ArrayDenormalizer
6 - ObjectNormalizer
Pour l’utilisation des normalizers, le composant utilise le Pattern Chain of Responsibility.
On peut bien sûr ajouter des decoders ou normalizers dans cette instance du serializer récupérée. Par exemple, pour ajouter un normalizer avec une priorité plus haute, on déclare un service dans la configuration de service de Symfony :
property_normalizer:
class: Symfony\Component\Serializer\Normalizer\PropertyNormalizer
public: false
tags: [serializer.normalizer]
Groups
Nous avons également la possibilité d’utiliser les groupes d’attributs pour sérialiser seulement une partie des propriétés d’un objet. Pour cela, il est nécessaire d’avoir le SensioFrameworkExtraBundle installé dans notre projet (composer require sensio/framework-extra-bundle).
Une fois installé, l’annotation Groups est utilisable pour les attributs de notre objet :
use Symfony\Component\Serializer\Annotation\Groups;
//...
/**
* @Groups({"seo"})
*/
private $metaDescription;
//...
Puis pour sérialiser ou désérialiser :
$productSerialized = $serializer->serialize($product, 'json',['groups' => 'seo']);
$productDeserialized = $serializer->deserialize($productSerialized, Product::class, 'json', ['groups' => 'seo']);
Attributs spécifiques
Il est aussi possible de spécifier les attributs que l’on souhaite sérialiser/désérialiser :
$productSerialized = $serializer->serialize($product, 'json', ['attributes' => ['title']]);
$productDeserialized = $serializer->deserialize($productSerialized, Product::class, 'json', ['attributes' => ['title']]);
De nombreuses ressources sont disponibles pour comprendre encore plus en profondeur ce composant avec la documentation mais aussi les différentes conférences PHP et Symfony. Pour une utilisation plus avancée de la sérialisation, surtout dans le cas d’une API, APIPlatform est vivement recommandé.
Sources :