• 50 hours
  • Hard

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 7/29/19

Introduction à la sérialisation avec JMSSerializer

Log in or subscribe for free to enjoy all this course has to offer!

 

Nous l'avons vu dans les chapitres précédents : ce qui est au cœur des APIs REST sont les ressources. Il s'agit de voir comment les manipuler : les envoyer à un client et sous quelle(s) forme(s) les envoyer, ainsi qu'en recevoir et comment les traiter dans notre application.

Installer JMSSerializer

Il est bien évidemment possible de sérialiser vos ressources à la main, néanmoins, il existe une librairie très pratique nous offrant toutes sortes d'options pour travailler aisément. Nous allons utiliser la librairie JMSSerializer au travers du bundle JMSSerializerBundle pour profiter de l'intégration facilitée des nombreuses fonctionnalités que comporte cette librairie tierce.

Je vous invite à créer un nouveau projet Symfony avec l'installer Symfony. Nous appellerons ce projet "blog-api". Une fois le projet installé, nous pouvons installer le bundle intégrant la librairie JMSSerializer.

Installons-le avec Composer. Pour ce faire, tapez la ligne de commande suivante :

$ composer require jms/serializer-bundle
Installation du JMS Serializer
Installation du JMSSerializer

Une fois l'installation terminée, il faut le déclarer dans la classe  AppKernel  (dans le dossier  app  de votre projet) :

<?php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = [
            // …
            new JMS\SerializerBundle\JMSSerializerBundle(),
        ];

        // …
    }
    // …
}

La ressource, le cœur de la sérialisation

Notre API va être en charge de manipuler des articles. Il nous faut donc une entitéArticlequi sera persistée en base de données. Nous utiliserons Doctrine pour la gestion en base de données. Créons donc un dossier  Entity  dans le  src/AppBundle  , puis notre classe  Article.

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table()
 */
class Article
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $title;

    /**
     * @ORM\Column(type="text")
     */
    private $content;

    public function getId()
    {
        return $this->id;
    }

    public function getTitle()
    {
        return $this->title;
    }

    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    public function getContent()
    {
        return $this->content;
    }

    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }
}

 Puis il nous faut créer notre base de données et monter notre table :

$ bin/console doctrine:database:create && bin/console doctrine:schema:create
Création de la base de données et des tables
Création de la base de données et des tables

Maintenant que notre ressource est prête, il s'agit de commencer doucement avec deux actions : sérialiser et désérialiser notre ressource. Concrètement, il s'agit de passer du mode linéarisé (JSON ou XML) au format délinéarisé (objet). Pour bien comprendre, voyons schématiquement ce que nous cherchons à faire :

Sérialisation & désérialisation d'objet
Sérialisation & désérialisation d'objet

Côté application PHP/Symfony, nous nous arrangerons toujours pour manipuler un objet PHP, en revanche, côté client de l'API, c'est le format linéarisé qui nous sera envoyé et que nous lui enverrons.

Premiers pas : la sérialisation et la désérialisation

Nous allons faire notre première sérialisation et désérialisation avec la librairie JMSSerializer. Cette librairie propose de nombreuses fonctionnalités :

  • linéariser (sérialisation) un graph d'objets (un objet peut en contenir d'autres, ce que l'on appelle un "graph d'objets") en chaîne de caractères (JSON, XML) ;

  • délinéariser (désérialisation) une chaîne de caractères pour obtenir un graph d'objets ;

  • la linéarisation est configurable en YAML, XML ou en annotations ;

  • l'intégration de JMSSerializer est native avec FOSRestBundle, nous le verrons dans la prochaine partie de ce cours.

Exerçons-nous à la sérialisation et la désérialisation. Nous allons donc créer deux actions (points d'entrée), juste pour l'exercice.

La sérialisation

ll s'agit de créer un objet php et de le transformer en format linéarisé pour l'envoyer à un client. En général, nous sommes dans ce cas d'utilisation lorsqu'il faut présenter des données. Ici, nous allons présenter les données d'un article. Pour l'instant, nous n'avons pas d'article en base de données, du coup, nous allons tout simplement créer un objet factice dans notre controller, le sérialiser, et le mettre en contenu de notre objet réponse.

N'oublions pas de respecter les contraintes de REST concernant :

  • l'URI qui doit être unique pour chaque ressource (ici, ce sera  /articles/{id} , même si nous n'utilisons pas encore l'ID pour aller chercher notre objet en base de données) ;

  • le contenu de notre réponse doit être auto-décrit, il nous faut donc indiquer dans la réponse quel type de données nous envoyons (nous indiquerons qu'il s'agit de JSON).

C'est parti, créons notre controller ainsi que l'action avec le code qui va bien :

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Article;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class ArticleController extends Controller
{
    /**
     * @Route("/articles/{id}", name="article_show")
     */
    public function showAction()
    {
        $article = new Article();
        $article
            ->setTitle('Mon premier article')
            ->setContent('Le contenu de mon article.')
        ;
        $data = $this->get('jms_serializer')->serialize($article, 'json');

        $response = new Response($data);
        $response->headers->set('Content-Type', 'application/json');

        return $response;
    }
}

Décomposons le code que nous venons d'écrire : dans un premier temps, nous créons un objet article factice, bon jusque là, rien de bien nouveau. Ligne 22, nous faisons appel au service  jms_serializer  nous permettant de faire appel à la méthode  serialize . Nous lui passons deux arguments : l'objet à sérialiser et le format dans lequel on souhaite sérialiser notre objet (ici, du JSON). Pour finir, nous construisons notre objet  Response  avec les données sérialisées et nous finissons par indiquer qu'il s'agit de JSON grâce à l'entête  Content-Type.

Testons maintenant le résultat pour voir ce qu'un client recevra. Nous allons utiliser Postman pour requêter notre application :

Requête à l'application avec Postman
Requête à l'application avec Postman

 Youpi ! La sérialisation s'est bien passée. :D

Et la désérialisation ?

Il s'agit maintenant de recevoir du JSON pour le transformer en objet. Complétons notre classe de controller avec une nouvelle action. Dans cette action, nous prenons soin de n'accepter que les requêtes en POST, car le cas d'utilisation est de créer un nouvel article. Nous allons donc désérialiser le JSON reçu, en obtenir un objet article, puis le persister en base de données :

<?php

namespace AppBundle\Controller;

//…
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;

class ArticleController extends Controller
{
   // …

    /**
     * @Route("/articles", name="article_create")
     * @Method({"POST"})
     */
    public function createAction(Request $request)
    {
        $data = $request->getContent();
        $article = $this->get('jms_serializer')->deserialize($data, 'AppBundle\Entity\Article', 'json');

        $em = $this->getDoctrine()->getManager();
        $em->persist($article);
        $em->flush();

        return new Response('', Response::HTTP_CREATED);
    }
}

A la ligne 19, nous récupérons le JSON envoyé dans le body de la requête pour pouvoir le désérialiser. Nous faisons à nouveau appel au service  jms_serializer  avec la méthode  deserialize  cette fois, et nous lui passons trois arguments :

  1. les données (une string) au format JSON,

  2. le namespace de la classe de l'objet que nous souhaitons obtenir (ici il s'agit de   AppBundle\Entity\Article ),

  3. le format de données que nous recevons (ici, il s'agit de JSON).

Testons maintenant la création d'article. Prenons Postman à nouveau pour poster les informations à notre application :

Poster les informations pour création d'un nouvel article via Postman
Poster les informations pour création d'un nouvel article via Postman

Superbe ! Nous obtenons bien notre réponse avec le code status 201 ! ^^

Oui oui, mais comment suis-je certain•e que l'article a bien été créé en base de données ?

Plutôt que d'aller voir directement en base de données, je vous propose de mettre à jour le code de notre première action pour aller chercher un article en fonction de l'id passé dans l'URL plutôt que de toujours retourner un article factice. Suivez le guide :

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Article;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class ArticleController extends Controller
{
    /**
     * @Route("/articles/{id}", name="article_show")
     */
    public function showAction(Article $article)
    {
        $data = $this->get('jms_serializer')->serialize($article, 'json');

        $response = new Response($data);
        $response->headers->set('Content-Type', 'application/json');

        return $response;
    }

    //…
}

Nous avons retiré la création de l'objet article factice pour laisser place à la récupération de l'article grâce au ParamConverter de Doctrine qui nous permet d'aller chercher un objet en base de données.

Voici le résultat :

Récupération d'un article en base de données
Récupération d'un article en base de données

Et voilà, nous avons avancé dans la création de notre CRUD d'articles !

Petit exercice : offrir la possibilité d'afficher la liste des articles

Mais oui ! Vous savez le faire ! Allez, je vous invite à créer une nouvelle action et vous inspirer de ce qui a été fait pour afficher un article.

Ça y est, vous avez fini ? Je vous propose cette correction :

<?php

namespace AppBundle\Controller;

// …

class ArticleController extends Controller
{
    // …

    /**
     * @Route("/articles", name="article_list")
     * @Method({"GET"})
     */
    public function listAction()
    {
        $articles = $this->getDoctrine()->getRepository('AppBundle:Article')->findAll();
        $data = $this->get('jms_serializer')->serialize($articles, 'json');

        $response = new Response($data);
        $response->headers->set('Content-Type', 'application/json');

        return $response;
    }
}

Facile ! Il suffit d'aller chercher l'ensemble des articles en base de données grâce à Doctrine et sérialiser la collection.

Nous avons les bases de l'utilisation de la librairie JMSSerializer ! :soleil: Bien évidemment, la sérialisation, ce n'est jamais un long fleuve tranquille… Je vous propose donc dès maintenant d'aller un peu plus loin avec l'utilisation de la librairie pour être paré en cas de besoins plus complexes.

Example of certificate of achievement
Example of certificate of achievement