L'objectif de ce module est de vous familiariser avec le développement d'API web en utilisant API Platform.
Ce TP va servir à mettre en place le projet et à présenter les grandes lignes d'API Platform.
API Platform fait partie de ces nombreux « frameworks » comportant une grande part de « magie ». De nombreux points du sujet consistent soit à vous familiariser avec la documentation, le fonctionnement et la philosophie d'API Platform soit à exécuter une commande. Prenez le temps de lire la documentation et/ou de comprendre ce que vous faites, l'intérêt n'est pas ici de vous faire réaliser un tutoriel, mais de vous amener à comprendre ce qu'il se passe.
Les notes des TP seront obtenues à partir de vos dépôts Git, vous devrez donc prendre grand soin de la qualité de ces dépôts et de leurs « commits ».
De manière similaire, les descriptions de vos « commits » devront être claires et informatives, afin de permettre l'évaluation de la progression de votre travail.
Le système de fichier du département informatique étant un peu poussif sur les accès aux fichiers. Et les projets Symfony sont constitués de nombreux fichiers. Vous êtes encouragé à travailler dans le répértoire « /working » de votre machine. Ce répertoire est un répertoire local à la machine, il n'utilise donc pas le système de fichier du département. En contrepartie, votre projet ne sera pas sauvegardé sur les serveurs du département et ne sera accessible que de cette machine. Vous penserez impérativement à sauvegarder votre travail sur le serveur GitLab au moins à chaque fin de séance.
Plusieurs solutions sont envisageables pour déployer une application API Platform. Nous retiendrons celle qui utilise l'outil symfony en ligne de commande (Symfony CLI).
Cette solution repose sur l'exécutable symfony et Composer. Vous commencerez par mettre à jour votre version de Composer, comme vous l'avez vu dans le TP de M. Cutrona. Si ce n'est pas déjà fait, installez ou mettez à jour l'exécutable symfony.
Vous pourrez ensuite créer un nouveau projet nommé r401-bookmarks-api
:
symfony new r401-bookmarks-api --version 7.2
Cette commande va créer un squelette d'application Symfony dans un nouveau répertoire api-bookmark
. Afin d'y ajouter Api Platform, dans le répertoire r401-bookmarks-api
, vous installerez les composants PHP d'API Platform, sans inclure les fichiers de configuration de Docker :
composer require api api-platform/core:^4.0
Votre API est maintenant installée et fonctionnelle.
La gestion de versions et l'archivage seront réalisés à l'aide de Git. Lors de la création du projet, l'exécutable symfony a initialisé un dépôt et effectué un premier « commit ». Pour un rapide tour d'horizon de Git, vous pouvez vous référerez au site de Monsieur Nourrit.
Vous pourrez ensuite indiquer à Git de suivre tous les fichiers actuellement dans votre répertoire :
git add .
Vous réaliserez le second « commit » de votre dépôt :
git commit -m "require api dependencies"
Vous associerez votre dépôt local à un dépôt distant, nommé r401-bookmarks-api
, sur le serveur GitLab du département. Vous penserez à ajouter votre intervenant de TP comme membre et à lui modifier ses droits comme « reporter ».
À partir de maintenant, vous réaliserez un « commit » après chaque question, avec un message clair et informatif, afin de permettre l'évaluation de la progression de votre travail.
Afin de gérer facilement la configuration de PhpStorm, le répertoire .idea
de configuration du projet devra être ignoré dans votre dépôt.
L'application API Platform est installée dans votre répertoire de travail, dans le cadre du développement de l'application, une solution simple pour l'afficher dans un navigateur consiste à utiliser le serveur web local de symfony.
Le serveur doit exposer comme racine du serveur public le sous-répertoire public
, la commande pour le démarrer est :
symfony serve
Si vous ouvrez l'URL http://127.0.0.1:8000
, vous constaterez que vous obtenez l'accès à l'application Symfony sous-jacente. L'API est accessible sous la ressource /api
de l'application Symfony. Pour l'instant votre API n'expose rien pour l'instant, puisque vous n'avez pas encore défini de données.
Afin de ne pas avoir à retenir la commande de lancement du serveur local, vous modifierez le fichier « composer.json » en ajoutant, dans la propriété scripts
de l'objet de configuration, une nouvelle propriété start
avec la commande comme valeur. Cette commande permettra de lancer le serveur de développement directement à l'aide de la commande suivante :
composer start
Afin d'éviter que la commande ne soit tuée au bout de 300 secondes, vous devez lever la restriction de durée d'exécution uniquement pour ce script.
Pour faciliter la prise en main de votre projet, décrivez la nouvelle commande Composer dans le fichier « composer.json ».
Notez que la liste des commandes peut être obtenue avec :
composer run --list
Les projets sont habituellement documentés pour permettre leur mis en route rapide. Ceci consiste à compléter un fichier Markdown « README.md » placé à la racine du projet.
Vous devez y faire figurer le nom de votre projet, votre nom en tant qu'auteur et donner les grandes lignes de l'installation, de la configuration et de la mise en route. Vous veillerez à garder ce fichier à jour tout au long du développement.
Votre API basée sur API Platform est un projet Symfony. Il convient donc de respecter le style de codage correspondant et PHP CS Fixer est l'outil officiel de Symfony et il est parfaitement intégré dans PhpStorm.
Vous devez en faire une dépendance de développement :
composer require --dev friendsofphp/php-cs-fixer
Vous configurerez votre éditeur pour qu'il utilise PHP CS Fixer pour formater votre code avec le fichier de configuration personnaliser fourni par Symfony : « .php-cs-fixer.dist.php » et vous penserez à exclure le répertoire « tests/Support/_generated » qui sera généré par Codeception à la fin de ce TP.
Vous réaliserez ensuite un « commit » pour marquer cette évolution du projet.
Afin de faciliter et systématiser son utilisation, vous ajouterez des scripts Composer :
php-cs-fixer fix --dry-run
php-cs-fixer fix
@test:cs
L'API ne comporte pas encore de données et ne propose donc pas encore d'opérations.
Dans un premier temps, vous allez configurer l'accès à la base de données. Vous commencerez par créer une nouvelle base de données MySQL pour l'occasion, VOTRELOGIN_Bookmarks
par exemple.
Ensuite, dans votre répertoire de travail, copiez le fichier « .env » dans le fichier « .env.local » puis modifiez la ligne correspondant à la connexion à la base de données :
Notez que la version du serveur est indispensable et peut être fixée dans l'URL ou dans le fichier « config/packages/doctrine.yaml ».
Afin d'avoir des données à exposer dans votre API, vous allez commencer par créer les tables permettant de stocker les données à afficher.
L'interaction avec la base de données se fera à l'aide des entités de Symfony. La gestion des entités sera réalisée à l'aide du générateur de Symfony. Vous installerez donc à l'aide de Composer le paquet PHP maker-bundle :
composer require --dev symfony/maker-bundle
Vous allez ensuite pouvoir créer l'entité représentant les données d'un bookmark :
bin/console make:entity
Vous l'appellerez Bookmark
et en ferez une ressource API Platform. Vous ajouterez ensuite les propriétés suivantes :
name
de type string
de taille 255
, ne pouvant pas être null
,
description
de type text
, ne pouvant pas être null
,
creationDate
de type datetime
, ne pouvant pas être null
,
isPublic
de type boolean
, ne pouvant pas être null
.
url
de type text
, ne pouvant pas être null
.
Vous allez pouvoir préparer la migration de la base de données, en suivant la procédure suivante :
Dans un premier temps, vous vérifierez la validité des instructions SQL à l'aide de l'option --dump-sql
de la sous-commande doctrine:schema:update
:
bin/console doctrine:schema:update --complete --dump-sql
Le résultat ne doit comporter que la création d'un table Bookmark
.
Si l'instruction SQL est valide, vous pouvez créer une migration de la base de données :
bin/console make:migration
Un nouveau fichier apparait dans le répertoire « migrations » de votre projet, n'oubliez pas de l'ajouter à votre dépôt Git.
Appliquez ensuite cette nouvelle migration avec la sous-commande doctrine:migrations:migrate
bin/console doctrine:migrations:migrate
La table devrait maintenant être créée dans la base, vous pouvez le vérifier à l'aide de phpMyAdmin ou en essayant de réaliser une requête sur la table :
bin/console dbal:run-sql "SELECT * FROM bookmark"
Vous devriez obtenir ce résultat :
[OK] The query yielded an empty result set.
Bookmark
.
Bookmark
dans la base de données.
Votre API expose maintenant des actions sur l'entité Bookmark
, mais celle-ci ne comportant pas de données, les interactions sont relativement limitées.
Dans le cadre du développement, il est fréquent d'avoir recours aux « fixtures », pour initialiser automatiquement un jeu de données dans une table. L'automatisation de l'opération permet de la re-exécuter chaque fois que l'on souhaite remettre la base à neuf.
Vous allez donc installer l'outil de gestion des « fixtures » :
composer require orm-fixtures --dev
Vous pourrez ensuite générer un nouveau script de génération de contenu pour la table que vous appellerez BookmarkFixtures
:
bin/console make:fixtures
Pour générer les données à insérer dans la base de données, de nombreuses solutions existent, nous allons utiliser un outil permettant de simplifier ce travail, Foundry :
composer require zenstruck/foundry --dev
Vous allez pouvoir ajouter une nouvelle forge de données pour l'entité Bookmark
:
bin/console make:factory
Dans la méthode de classe default
, vous allez fournir des valeur par défaut pour chacun des attributs d'un bookmark. À l'exception de id
qui est autogénéré.
En utilisant Faker, vous initialiserez les attributs comme suit :
name
: un faux nom de société.
description
: un paragraphe de texte généré.
creationDate
: une date entre maintenant et il y a deux ans.
isPublic
: un booléen.
url
: une fausse url.
Pour générer un jeu de données dans la base de données, vous n'avez plus qu'à simplement remplacer le contenu de la méthode load
de BookmarkFixtures
par le code suivant :
Vous pourriez ensuite, mais vous ne le ferez pas, remplir la base de données à l'aide de la commande suivante :
bin/console doctrine:fixtures:load --purge-with-truncate
Cette commande ne réinitialise pas les séquences des identifiants des tables et, si vous l'utilisez plusieurs fois de suite, les identifiants ne cesseront d'augmenter. Pour palier ce problème, et s'assurer d'avoir une base de données « toute neuve » à chaque fois, vous allez écrire un script Composer « db » qui recrée la base de données en enchaînant les commandes :
Vous documenterez évidemment ce nouveau script dans le fichier « README.md »
Encore une fois, vous pouvez vérifier que tout fonctionne correctement soit à l'aide de phpMyAdmin, soit en réalisant une requête :
bin/console dbal:run-sql "SELECT COUNT(*) FROM bookmark"
Vous devriez obtenir ce résultat :
---------- COUNT(*) ---------- 20 ----------
orm-fixtures
et Foundry
.
Bookmark
: BookmarkFactory
.
BookmarkFixtures
.
Bookmark
.
Bien que fonctionnelle, cette solution n'est pas toujours satisfaisante, car les données générées ne sont forcément suffisamment réaliste, notamment lors du développement de l'application consommant l'API ou lors des tests.
Dans ce cas, on a généralement recours à un jeu de données construit à la main, avec lequel on remplit la base de données.
Le jeux de données étant relativement petit et afin de vous simplifier la lecture des données, vous allez utiliser des données au format JSON. Vous enregistrerez le fichier
« bookmarks.json » (télécharger)
dans un sous-répertoire data
du répertoire DataFixtures
. En utilisant file_get_contents et json_decode, vous chargerez le tableau contenant les données depuis le fichier, puis vous créerez un bookmark par ligne du tableau en lui fournissant les propriétés url
, name
et description
de chaque élément du tableau.
Vous devez faire référence au fichier bookmark.json
depuis le fichier BookmarkFixtures.php
. Vous ne pouvez pas utiliser le répertoire courant « .
» qui fait référence au programme en cours d'exécution. Vous devez utiliser la constante magique qui donne le répertoire du fichier dans lequel elle est utilisée.
Après avoir chargé à nouveau vos « fixtures », vous pouvez vérifier le bon fonctionnement à l'aide de la requête suivante :
bin/console dbal:run-sql "SELECT COUNT(*) FROM bookmark"
Vous devriez obtenir ce résultat :
---------- COUNT(*) ---------- 67 ----------
Bookmark
.
En vous rendant sur la page de votre API, vous constatez que par défaut, API Platform vous propose une représentation CRUD de vos entités, en utilisant le nom de l'entité comme nom de ressource.
Vous expérimenterez les différentes actions disponibles en créant de nouveaux bookmarks, en les modifiant, en en supprimant et en comparant le listage d'une collection ou d'un élément.
En testant un peu l'opération de listage des bookmarks, vous constaterez qu'ils sont triés selon leur id, ce qui n'a pas vraiment de sens. Vous remédierez à cela en ajoutant un attribut permettant de spécifier l'ordre par défaut, ici par ordre alphabétique des noms.
Vous ajouterez des options à votre collection sous la forme de tris et de filtres.
Ici, vous ajouterez trois types de filtres, mais vous pouvez librement explorer les filtres disponibles :
Vous avez dû remarquer que le test manuel des fonctionnalités est fastidieux. Afin d'améliorer la DX (expérience de développement) de votre projet, et surtout fiabiliser vos développements, vous allez mettre en place des tests automatisés. Il est possible d'effectuer des tests sur une application Symfony avec PHPUnit mais nous lui préférerons Codeception qui propose un module REST que nous avons adapté à API Platform.
Vous commencerez par installer la suite de tests de Codeception ainsi que les modules qui vous seront utiles :
composer require --dev --no-interaction \ codeception/codeception \ codeception/module-asserts \ codeception/module-symfony \ codeception/module-doctrine \ codeception/module-rest
Vous devez ensuite initialiser Codeception :
php vendor/bin/codecept bootstrap --namespace=App\\Tests --empty
Vous pourrez ensuite modifier l'environnement de tests en y ajoutant une base de données SQLite, dans le fichier « .env.test » à la racine de votre projet :
Vous configurerez Codeception pour qu'il utilise les fichiers « .env » et « .env.test » comme sources de paramètres dans « codeception.yml »
Votre première suite des tests s'appellera « Api
» et sera générée par :
php vendor/bin/codecept generate:suite Api
Codeception génère du code PHP. Malheureusement, ce code n'est pas toujours valide au sens des règles fixées pour PHP CS Fixer. C'est pourquoi vous ajouterez une exclusion pour le répertoire tests/Support/_generated dans le fichier « .php-cs-fixer.dist.php »
Vous devez créer la base de données de test et y ajouter les tables. Cette démarche devant être facilement accessible lors du clonage du projet, vous allez définir un nouveau script Composer « test:codeception
» qui contiendra les commandes suivantes :
Vous ajouterez un appel à ce script Composer dans le script « test
», après l'appel au script « test:cs
».
Vous allez maintenant pouvoir ajouter des tests à votre projet, permettant d'automatiser vos interactions avec l'API pour tester les fonctionnalités que vous mettez en place. Les tests ne restent cependant que des garde-fous, ils ne remplacent pas un test réel lorsqu'une fonctionnalité est terminée.
Codeception propose un module REST rendant aisément accessibles les envois de données selon les diverses méthodes HTTP (POST, PUT, … mais sans ajouter l'en-tête Content-Type correspondant aux données JSON) et proposant un accès et des contrôles facilités sur les réponses HTTP dont les contenus sont JSON ou XML. Cependant, certains tests couramment utiles pour API Platform nécessitent une succession d'appels de méthodes du module REST. C'est pourquoi nous vous proposons un module développé en interne qui étend le module REST et qui permet de faciliter l'écriture des tests pour API Platform. Il précise en particulier les en-têtes Content-Type adéquats selon la méthode HTTP.
Placez le module « ApiPlatform » (télécharger) dans le répertoire tests/Support/Helper.
Pour résoudre des soucis d'accès à l'EntityManager
au cours des tests entre le moteur de test et le navigateur de symfony, vous ajouterez un autre module développé en interne permettant de réinitialiser l'EntityManager
entre chaque test.
Placez le module « EntityManagerReset » (télécharger) dans le répertoire tests/Support/Helper.
Vous activerez ensuite tous les modules nécessaires dans la suite Api en modifiant la propriété modules du fichier « Api.suite.yml » comme suit :
Vous placerez dans un répertoire tests/Api/Bookmark la classe de Cests suivante : « BookmarkGetCest.php » (télécharger)
Cette classe de Cests permet de tester l'accès à un signet. Lors de l'exécution de la commande suivante, le test devraient être valide avec toutes ses assertions :
composer test
Il est possible que vos tests échouent sur la propriété isPublic
. Il est possible que le maker d'entité de Symfony est raté la construction de l'accesseur en omettant de doublé le premier is
. Vous prendrez soin de modifier l'accesseur de la propriété isPublic
en isIsPublic
.
Une fois l'environnement de test fonctionnel, vous ajouterez un nouveau jeu de tests pour la collection de signets et vous ferrez en sorte que les tests soient tous valides :« tests/Api/Bookmark/BookmarkGetCollectionCest.php » (télécharger).
Bookmark
.
Bookmark
.