Dans ce TD, nous nous baserons sur le code écrit dans le TD1. Maintenant que nous pouvons publier des messages en récupérant les coordonnées des adresses, nous allons exposer notre propre API, pour permettre aux utilisateurs de publier et de consulter les messages.
Avant d'aller plus loin, nous allons stocker les dates de création des messages. Lancez :
symfony console make:entity
Et ajoutez un champ date de type datetime (qui peut être null). Lancez :
symfony console doctrine:schema:update --force
Pour mettre à jour votre base de données.
Ajoutez le constructeur suivant dans Message pour que la date initiale soit celle de création:
public function __construct()
{
$this->date = new \DateTime();
}
Enfin, ajoutez un message et vérifiez que la date est bien enregistrée.
À l'aide de la commande:
symfony console make:controller
Créez un contrôlleur ApiController. Ajoutez l'attribut #[Route('/api')] à la classe. De cette manière,
toutes les routes de ce contrôlleur seront préfixées par /api/.
C'est ici que nous hébergerons nos endpoints !
Créez tout d'abord un premier endpoint, dont voici les caractéristiques:
/api/messagesGETLa réponse aura cette forme :
{
"messages": [
{
"id": 17,
"text": "Bonjour à tous!",
"date": "2023-01-16T16:14:14+01:00"
},
...
]
}
(La date est ici formatée en ISO 8601)
Conseil: avec Symfony il est possible de construire un tableau PHP $data et d'utiliser:
return $this->json($data)
Avant d'aller plus loin, nous allons utiliser des outils qui permettront de nous simplifier la tâche. Premièrement, installez :
symfony composer require friendsofsymfony/rest-bundle
Éditez alors config/packages/fos_rest.yaml de cette façon:
# config/packages/fos_rest.yaml
fos_rest:
view:
view_response_listener: true
format_listener:
rules:
- { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json ] }
- { path: '^/', priorities: ['text/html', '*/*'], fallback_format: html, prefer_extension: true }
Et ajoutez l'attribut View (de FOS\RestBundle\Controller\Annotations\View) à votre action, qui pourra
devenir :
#[View()]
#[Route('/messages', name: 'app_api')]
public function index(MessageRepository $messagesRepository)
{
return [
"messages" => $messagesRepository->findAll()
];
}
Constatez que les messages sont désormais sérialisés automatiquement !
La sérialisation automatique est très pratique, mais elle expose par défaut tous les champs de notre message, ce qui pourrait être inutile, ou même divulguer des informations que l'on ne souhaite pas.
Pour ce faire, il est possible de définir ce que l'on appelle des groupes de sérialisation.
À l'aide de la documentation, créez un groupe de sérialisation message_basic qui ne contient
que les informations "basiques" (l'id, le texte et la date), et modifiez votre attribut View pour
utiliser ce groupe.
Documentation :
Nous allons pouvoir enfin assembler ce que nous avons fait de manière à apporter une première fonction de Géo-chat !
Modifiez le endpoint /api/messages, de manière à ce que :
address obligatoire: une chaîne de caractère donnant l'adresse autour de laquelle on souhaite
obtenir les messagesradius: un rayon (par défaut 2000) en mètres utilisé pour la rechercheLes erreurs suivantes seront gérées:
Elle donneront lieu à des erreurs HTTP de type 400 (Bad request).
Les distances seront fournies dans la réponse, exemple :
{
"results": [
{
"message": {
"text": "Un bonjour du département #iutinformatique"
},
"distance": 386.33058427448344
}
],
...
}
Vous pourrez utiliser la méthode MessageRepository::findClose(), qui vous aidera à calculer les distances
entre les points.
Vous pouvez utiliser
MapQueryParameter
afin de gérer plus efficacement les paramètres address et radius.
Ajoutez un paramètre posted_after optionnel au endpoint /api/messages, qui filtre les messages qui
ont été postés uniquement après une date donnée.
Créez un nouveau endpoint permettant de publier des messages :
/api/messagePOSTCet appel créera un message dans la base de données. Pour l'instant, ne vous occupez pas des erreurs, et répondez :
{
"message": {
"id": 34,
"text": "Coucou !",
"date": "2023-01-16T18:15:57+01:00"
}
}
Pour cela, vous utiliserez MapRequestPayload, afin de désérialiser le message reçu en JSON.
Pour éviter que Doctrine essaie de convertir automatiquement les requêtes, vous pouvez désactiver cette conversion dans config/packages/sensio_framework_extra.yaml:
sensio_framework_extra:
request:
auto_convert: false
Maintenant, nous allons gérer les contraintes suivantes :
text ne soit pas videaddress ne soit pas videPour cela :
Message à l'aide de l'attribut #[Assert\...] du
validateur de Symfony
Implémentez maintenant un endpoint permettant de mettre à jour un message :
/message/{id}PATCHCe endpoint fonctionnera exactement comme le précédent, mais en mise à jour. Vous êtes invité à factoriser le plus de code possible !
Ajoutez maintenant un endopoint permettant de supprimer un message :
/message/{id}DELETECet appel détruit le message passé en paramètre. Pour l'instant, notez que :