Développer une API REST avec Symfony 3

Logo SymfonySymfony (2 et 3) permet de publier très facilement une API REST à l’aide de différents bundles.
Cette API peut être également fourni d’une documentation générée dynamiquement.

L’idée est de créer une API REST simple, qui met à disposition une liste d’utilisateurs et de commentaires.
Commençons par générer un nouveau projet.

cd /var/www/
symfony new symfony-rest
cd symfony-rest/

Les bundles qui vont nous permettre d’avoir une API fonctionnelle sont les suivants : FOSRestBundle et JMSSerializerBundle.
Il faut donc les ajouter via Composer et les renseigner dans le tableau de la fonction registerBundles du fichier app/AppKernel.php.

composer require friendsofsymfony/rest-bundle
composer require jms/serializer-bundle
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle(),

Nous allons ensuite définir les paramètres du bundle FOSRestBundle dans le fichier app/config/config.yml.

# FOSRestBundle
fos_rest:
    param_fetcher_listener: true
    body_listener: true
    format_listener: true
    view:
        view_response_listener: 'force'
        formats:
            xml: true
            json : true
        templating_formats:
            html: true
        force_redirects:
            html: true
        failed_validation: HTTP_BAD_REQUEST
        default_engine: twig
    routing_loader:
        default_format: json

Il nous reste à définir le routing dans un fichier séparé.
Déclaration de l’import du nouveau fichier dans app/config/routing.yml, en préfixant l’url par « /api/ »

api:
    resource: api_routes.yml
    type: rest
    prefix: /api/

Contenu du fichier app/config/api_routes.yml :

users:
    resource: AppBundle\Controller\UsersController
    type:     rest

comments:
    resource: AppBundle\Controller\CommentsController
    type:     rest

Il nous faut créer une entity User et son repository, avec les champs dont vous avez besoin (nom, email, etc.).
Puis écrire le contenu de nos controllers, voici un exemple avec UsersController.php

namespace AppBundle\Controller;

use AppBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class UsersController extends Controller
{
    /**
     * @return array
     */
    public function getUsersAction()
    {
        $em = $this->getDoctrine()->getManager();
        $users = $em->getRepository('AppBundle:User')->findAll();

        return array('users' => $users);
    }

    /**
     * @param User $user
     * @return array
     */
    public function getUserAction(User $user)
    {
        return array('user' => $user);
    }
}

Ce bout de code nous permet de mettre à disposition la liste des utilisateurs, ou bien un utilisateur précis.
En accédant à http://localhost/symfony-rest/api/users, l’API REST retourne une collection d’utilisateurs au format JSON.
Vous pouvez avoir cette même liste au format XML en suffixant l’URL avec « .xml ».
En appelant URL suivante http://localhost/symfony-rest/api/users/1, le serveur retournera l’utilisateur défini par l’identifiant 1.

{
    "users": [
        {
            "id": 1,
            "email": "jdoe@domain.com",
            "name": "John Doe",
            "password": "449bf6759f52bc0d1b7953f9fc2aaf32"
        },
        {
            "id": 2,
            "email": "jdalton@domain.com",
            "name": "Joe Dalton",
            "password": "04bd53ec196f6de5101ab3a56f12d0a6"
        }
    ]
}
{
    "user": {
        "id": 1,
        "email": "jdoe@domain.com",
        "name": "John Doe",
        "password": "449bf6759f52bc0d1b7953f9fc2aaf32"
    }
}

Par défaut l’API retourne tous les champs de notre entity. Vous avez la possibilité de préciser soit par inclusion, soit par exclusion, les champs que vous souhaitez envoyer.
Exemple dans notre cas en excluant le champ password :

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Exclude;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * User
 *
 * @ORM\Table(name="User")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 * @ExclusionPolicy("none")
 */
class User
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name")
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="email")
     */
    private $email;

    /**
     * @var string
     *
     * @ORM\Column(name="password")
     * @Exclude
     */
    private $password;

    //......

Il nous reste maintenant à mettre à disposition une documentation pour les personnes qui voudraient éventuellement utiliser notre API.

Pour cela nous allons utiliser le bundle NelmioApiDocBundle qui se base sur l’outil Swagger pour générer une documentation pratique et lisible.
De notre côté, il faudra renseigner les informations utiles à la documentation dans le PHPDoc de chaque fonction de nos controllers.

Dans un premier temps il faut ajouter le bundle à notre configuration et le paramétrer.

composer require nelmio/api-doc-bundle

On l’ajoute au fichier app/AppKernel.php

new NelmioApiDocBundleNelmioApiDocBundle(),

Petite ligne de configuration dans le fichier app/config/config.yml

nelmio_api_doc: ~

Et enfin le routing qui va bien : app/config/routing.yml

NelmioApiDocBundle:
    resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
    prefix:   /api/doc

Voici maintenant à quoi ressemble le PHPDoc de nos fonctions dans UsersController.php.

namespace AppBundle\Controller;

use AppBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;

class UsersController extends Controller
{
    /**
     * Cette fonction retourne tous les utilisateurs
     * Possibilité de décrire la fonction plus en détails
     *
     * @ApiDoc(
     *  resource=true,
     *  description="Retourne les utilisateurs"
     * )
     *
     * @return array
     */
    public function getUsersAction()
    {
        $em = $this->getDoctrine()->getManager();
        $users = $em->getRepository('AppBundle:User')->findAll();

        return array('users' => $users);
    }

    /**
     * Cette fonction retourne un utilisateur
     * Possibilité de décrire la fonction plus en détails
     *
     * @ApiDoc(
     *  description="Retourne un utilisateur",
     *  parameters={
     *      {"name"="id", "dataType"="integer", "required"=true, "description"="identifiant de l'utilisateur"}
     *  }
     * )
     *
     * @param User $user
     * @return array
     */
    public function getUserAction(User $user)
    {
        return array('user' => $user);
    }
}

Pour créer des fonctionnalités POST/DELETE/PUT, qui sont les verbes qui caractérisent une application RESTful, il suffit de préfixer le nom de votre fonction comme ceci : postUserAction(), putUserAction() etc.

Désormais, vous pouvez partager la documentation de votre API à cette adresse http://localhost/symfony-rest/api/doc.

Voici à quoi ressemble le rendu final :

Documentation API avec Swagger

Une étape supplémentaire sera de gérer une identification utilisateur sécurisée via OAuth2.
Peut-être dans un prochain billet :)

Share Button

Un commentaire

  1. Super article, serait-il possible d’avoir un autre billet avec l’identification utilisateur sécurisée via OAuth ? ;-)

    Merci

    Reply
  2. bonjour
    merci pour votre tutoriel je voudrais savoir dans quoi on peut utiliser cette documentation de l’api et comment utiliser le RESTapi pour interagir avec un autre framework comme ionic ou cordova

    Reply
  3. Bonjour et merci de ce tuto clair.

    Je m’adresse à vous en désespoir de cause, vue votre compétence manifeste.
    Je suis absolument novice dans l’usage de Symfony et – par vice – j’ai voulu bêtement tester l’intégration du « petshop » swagger. Je me suis naïvement rendu sur le editor.swagger.io et j’ai récupérer le bundle php-symfony (server side) en me disant que j’allais pouvoir l’intégrer fastoche dans symfony.
    Or tous ce que je tente échoue même (et surtout) en suivant les instructions du Readme.md :

    ———————————————————–
    To install the dependencies via [Composer](http://getcomposer.org/), add the following repository to `composer.json` of your Symfony project:

    « `json
    {
    « repositories »: [{
    « type »: « path »,
    « url »: « //Path to your generated swagger bundle »
    }],
    }
    « `

    Then run:

    « `
    composer require swagger/server-bundle:dev-master
    « `
    —————————————————-

    Je ne sais pas de quelle url on parle puisque j’ai téléchargé le bundle. Et l’instruction composer require swagger/server-bundle:dev-master me retourne

    [InvalidArgumentException] Could not find a matching version of package swagger/server-bundle. Check t he package spelling, your version constraint and that the package is availa ble in a stability which matches your minimum-stability (stable).

    En fait, je ne sais pas trop où installer le bundle telecharger et par defaut je l’ai mis dans www/monapplication/vendor/swagger/server-bundle comme semble l’indiquer le message d’erreur mais ça ne marche pas mieux.
    Les explications que j’ai trouvées semblent s’adresser à ceux qui savent déjà. Vos lumières m’aideraient. Merci.

    Reply

Laisser un commentaire.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.