API Livres/Auteurs

Pour fabriquer une API nous pouvons le faire :

  • à la Main

  • avec APIPlatform

API livres et auteurs « à la main »

Avant de commencer nous allons installer le bundle nelmio/cors-bundle :

composer req cors

ou

composer require nelmio/cors-bundle

L’installation se lance.

Using version ^2.0 for nelmio/cors-bundle
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Restricting packages listed in "symfony/symfony" to "5.0.*"
Package operations: 1 install, 0 updates, 0 removals
- Installing nelmio/cors-bundle (2.0.1): Downloading (100%)
Writing lock file
Generating autoload files
ocramius/package-versions: Generating version class...
ocramius/package-versions: ...done generating version class
Symfony operations: 1 recipe (b3d83b533e25e4f0b71e3e2b85c51c5d)
- Configuring nelmio/cors-bundle (>=1.5): From github.com/symfony/recipes:master
Executing script cache:clear [OK]
Executing script assets:install public [OK]

Some files may have been created or updated to configure your new packages.
Please review, edit and commit them: these files are yours.

Le cors-bundle permet de définir les règles CORS. Ce bundle permet également de définir les domaines qui auront accès à votre API REST.

Vérifions que les règles ont bien été définies dans /config/packages/nelmio_cors.yaml

nelmio_cors:
    defaults:
        origin_regex: true
        allow_origin: ['%env(CORS_ALLOW_ORIGIN)%']
        allow_methods: ['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']
        allow_headers: ['Content-Type', 'Authorization']
        expose_headers: ['Link']
        max_age: 3600
    paths:
        '^/': null

Dans .env modifions le paramètre d’autorisation des domaines.

###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=*
###< nelmio/cors-bundle ###

Ce paramètre permet à n’importe quel domaine d’accéder à notre API. /!A n’utiliser que pour des APIs puliques /!

Ajoutons un controleur d’Auteur et un autre de Livre avec :

php bin/console make:controller AuteurController --no-template
php bin/console make:controller LivreController --no-template

On obtient déjà par exemple pour AuteurController un index qui renvoie un contenu json :

<?php

   namespace App\Controller;

   use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
   use Symfony\Component\Routing\Annotation\Route;

   class AuteurController extends AbstractController
   {
       /**
       * @Route("/auteur", name="auteur")
       */
       public function index()
       {
           return $this->json([
               'message' => 'Welcome to your new controller!',
               'path' => 'src/Controller/AuteurController.php',
           ]);
       }
   }

Lister tous les auteurs

Complétons AuteurController :

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use App\Entity\Auteur;

/**
* @Route("/books/api/v1.0")
*/
class AuteurController extends AbstractController
{

    /**
    * Permet d'avoir la liste de tous les auteurs
    * @Route("/auteur", name="liste_auteur", methods={"GET"})
    */
    public function listeAuteur()
    {
        $repository   = $this->getDoctrine()->getRepository(Auteur::class);
        $listeAuteur  = $repository->findAll();
        $listeReponse = array();
        foreach ($listeAuteur as $auteur) {
                $listeReponse[] = array(
                'id'     => $auteur->getId(),
                'nom'    => $auteur->getNom(),
                'prenom' => $auteur->getPrenom(),
            );
        }
        $reponse = new Response();
        $reponse->setContent(json_encode(array("auteur"=>$listeReponse)));
        $reponse->headers->set("Content-Type", "application/json");
        $reponse->headers->set("Access-Control-Allow-Origin", "*");
        return $reponse;
    }

Puis pour avoir les détails d’un auteur

<?php
/**
 * Permet d'avoir les livre d'un auteur grâce à son id
 * @Route("/auteur/{id}", name="details_auteur", methods={"GET"})
 */
public function detailsAuteur($id)
{
    $repository = $this->getDoctrine()->getRepository(Auteur::class);
    $auteur     = $repository->find($id);
    $listeLivre = $auteur->getLivres();
    $livres = [];
    foreach ($listeLivre as $livre) {
        $livres[] = array(
            "id"    => $livre->getId(),
            "titre" => $livre->getTitre(),
        );
    }
    $reponse = new Response(json_encode(array(
                    'id'     => $auteur->getId(),
                    'nom'    => $auteur->getNom(),
                    'prenom' => $auteur->getPrenom(),
                    'livres' => $livres,
                    ))
            );
    $reponse->headers->set("Content-Type", "application/json");
    $reponse->headers->set("Access-Control-Allow-Origin", "*");
    return $reponse;
}

Créer un auteur

<?php
/**
 * Permet de créer un auteur
 * @Route("/auteur", name="nouveau_auteur", methods={"POST"})
 */
public function nouveauAuteur(Request $request)
{
    $entityManager = $this->getDoctrine()->getManager();
    $auteur = new Auteur();
    $body   = json_decode($request->getContent(), true);
    $nom    = $body['nom'];
    $prenom = $body['prenom'];
    $auteur->setNom($nom);
    $auteur->setPrenom($prenom);
    $entityManager->persist($auteur);
            $entityManager->flush();

    $reponse = new Response(json_encode(array(
                    'id'     => $auteur->getId(),
                    'nom'    => $auteur->getNom(),
                    'prenom' => $auteur->getPrenom()
                    )
            ));

    $reponse->headers->set("Content-Type", "application/json");
    $reponse->headers->set("Access-Control-Allow-Origin", "*");
    return $reponse;
}

Supprimer un auteur

<?php
/**
 * Permet de supprimer un auteur grâce à son id
 * @Route("/auteur", name="suppression_auteur", methods={"DELETE"})
 */
public function suppressionAuteur(Request $request)
{
    $entityManager = $this->getDoctrine()->getManager();
    $repository    = $this->getDoctrine()->getRepository(Auteur::class);
    $body          = json_decode($request->getContent(), true);
    $id            = $body['id'];
    $auteur        = $repository->find($id);
    $entityManager->remove($auteur);
            $entityManager->flush();
            $reponse = new Response(json_encode(array(
                    'nom'    => $auteur->getNom(),
                    'prenom' => $auteur->getPrenom(),
                    ))
            );
    $reponse->headers->set("Content-Type", "application/json");
    $reponse->headers->set("Access-Control-Allow-Origin", "*");
    return $reponse;
}

Modifier un auteur

<?php
/**
 * Permet de modifier le nom et/ou le prenom d'un auteur grâce à son id
 * La gestion des livres de l'auteur se fera via l'entité livre
 * @Route("/auteur", name="modification_auteur", methods={"PUT"})
 */
public function modificationAuteur(Request $request)
{
    $entityManager = $this->getDoctrine()->getManager();
    $repository    = $this->getDoctrine()->getRepository(Auteur::class);
    $body          = json_decode($request->getContent(), true);
    $id            = $body['id'];
    $nom           = $body['nom'];
    $prenom        = $body['prenom'];
    $auteur        = $repository->find($id);
    $auteur->setNom($nom);
    $auteur->setPrenom($prenom);
    $entityManager->persist($auteur);
            $entityManager->flush();
            $reponse = new Response(json_encode(array(
                            'id'     => $auteur->getId(),
                    'nom'    => $auteur->getNom(),
                    'prenom' => $auteur->getPrenom(),
                    ))
            );
    $reponse->headers->set("Content-Type", "application/json");
    $reponse->headers->set("Access-Control-Allow-Origin", "*");
    return $reponse;
}

L’API/CRUD de l’entité Auteur est terminée. Faites les mêmes manipulations pour l’entité Livre : lister tous les livres, détails d’un livre, créer un livre, supprimer un livre et modifier un livre. N’oubliez pas le livre stock l’auteur.

Notre API est complète mais nous avons du travailler dur … Nous aurions pu prendre une solution de facilité !

Avec le composant d’API APIPlatform

Installation d’APIPlatform

On l’installe avec la recette flex correspondante :

composer req api

Annotons les entités Auteur et Livres en ajoutant l’import :

<?php

    use ApiPlatform\Core\Annotation\ApiResource;

et l’annotation:

/**
* @ApiResource()
*/

Voilà !

Visitons la page : API

Notre API est prête à l’emploi :

_images/APIPlatform.png

GraphQL

GraphQL permet de faire des requêtes plus complexes sur une API et permet donc d’interopérer de façon plus complète avec cette API. Voir GraphQL .

Pour activer le support GraphQL dans Symfony :

composer require webonyx/graphql-php,

Puis visiter : /api/graphql

Consultez la documentation d”APIPlatform

FriendsOfSymfony REST Bundle

Une autre solution pour fabriquer un service REST est de passer par un autre Bundle connu : FOS (Friends Of Symfony). On installe le Bundle REST correspondant avec:

composer require friendsofsymfony/rest-bundle

Un exemple complet se trouve sur : Symfony5ApiRest