A présent, récupérons une catégorie avec son id, en utilisant directement le type de l’entité (principe du ParamConverter pour directement binder son id avec le champ correspondant de la base et éviter de coder nous-mêmes le renvoi à une page 404)
/**
* @Rest\Get(
* path = "/{id}",
* name="app_api_category",
* requirements={"id"="\d+"}
* )
*
* @Doc\ApiDoc(
* section="Categories",
* resource=true,
* description="Get one category.",
* requirements={
* {
* "name"="id",
* "dataType"="integer",
* "requirement"="\d+",
* "description"="The category unique identifier."
* }
* },
* statusCodes={
* 200="Returned when successful",
* }
* )
*/
public function getCategoryAction(Category $category)
{
return $category;
}
Le verbe POST
C’est parti pour le POST. Le code final va être celui-ci:
/**
* @Rest\Post("/", name="app_api_new_category")
*
* @ParamConverter("category", converter="fos_rest.request_body")
*
* @Rest\View(statusCode=201)
*
* @Doc\ApiDoc(
* section="Categories",
* description="Creates a new category.",
* statusCodes={
* 201="Returned if category has been successfully created",
* 400="Returned if errors",
* 500="Returned if server error"
* }
* )
*/
public function postCategoryAction(Category $category, ConstraintViolationListInterface $violations)
{
if (count($violations)) {
return $this->view($violations, 400);
}
$this->get('app_core.category_manager')->save($category);
return $this->view(null, 201,
[
'Location' => $this->generateUrl('app_api_category', [ 'id' => $category->getId()]),
]);
}
Plusieurs choses:
- j’utilise la classe Symfony\Component\Validator\ConstraintViolationListInterface. N’oubliez pas de faire un use en haut du fichier
- Je fais appel à un manager d’entité qui va sauver la nouvelle catégorie.
Le manager de Category
<?php
namespace App\CoreBundle\Entity\Manager;
use App\CoreBundle\Entity\Category;
use Doctrine\Common\Persistence\ObjectManager;
class CategoryManager
{
public function __construct(ObjectManager $em) {
$this->em = $em;
}
public function save(Category $category)
{
$this->em->persist($category);
$this->em->flush();
}
}
Ok, vous pourriez me dire, si c’était pour faire ça, l’utilité d’un manager était vraiment superflue.
En effet, vous auriez raison. Je l’ai fait dans l’hypothèse que j’y injecterai des dépendances autres que l’ObjectManager (comme le dispatcher). Je préfère le faire dès le début que de perdre du temps à créer mon manager par la suite.
L’interface ConstraintViolationListInterface
Celle-là, je la trouve puissante, car elle va aller analyser notre validation d’entité (par annotation, en php, etc.) pour valider l’objet sans que l’on ait à le faire dans le contrôleur. Et ça, c’est vraiment puissant.
Dans PostMan, nous transmettons alors un array avec le titre:
Nous voyons que le code 201 est renvoyé, avec l’indication « created ». Dans les headers, la location indique l’url de la nouvelle catégorie créée.
Le verbe PUT
Il est temps de pouvoir mettre à jour notre entité. Attention, j’attire votre attention sur le fait que PUT ne prend que les valeurs transmises et met à null les autres. Si vous voulez ne mettre à jour que certains champs et laisser les autres tels qu’ils étaient, vous devez utiliser PATCH. C’est une différence extrêmement importante. Ici, comme j’ai juste le titre, et qu’il est obligatoire, j’utilise PUT.
/**
* @Rest\Put(
* path = "/{id}",
* name = " app_api_edit_category",
* requirements = {"id"="\d+"}
* )
*
* @ParamConverter("apiCategory", converter="fos_rest.request_body")
*
* @Doc\ApiDoc(
* section="Categories",
* description="Edit an existing category.",
* statusCodes={
* 201="Returned if category has been successfully edited",
* 400="Returned if errors",
* 500="Returned if server error"
* },
* requirements={
* {
* "name"="id",
* "dataType"="integer",
* "requirement"="\d+",
* "description"="The category unique identifier."
* }
* },
* )
*/
public function putCategoryAction(Category $category, Category $apiCategory,
ConstraintViolationListInterface $violations)
{
if (count($violations)) {
return $this->view($violations, 400);
}
$this->get('app_core.category_manager')->update($category, $apiCategory);
$em = $this->getDoctrine()->getManager();
$em->flush($category);
return $this->view('', Response::HTTP_NO_CONTENT);
}
Ici, je récupère la category existante par l’id, et je récupère les informations passées dans la request par apiCategory.
Puis je fais appel au manager qui va mettre à jour les champs de Category avec ceux de ApiCategory.
<?php
namespace App\CoreBundle\Entity\Manager;
use App\CoreBundle\Entity\Category;
use Doctrine\Common\Persistence\ObjectManager;
class CategoryManager
{
public function __construct(ObjectManager $em) {
$this->em = $em;
}
public function update(Category $category, Category $apiCategory)
{
$category->update($apiCategory);
$this->em->flush();
}
public function save(Category $category)
{
$this->em->persist($category);
$this->em->flush();
}
}
Et enfin, dans mon entité Category, je rajoute la fonction update:
public function update(Category $category)
{
$this->title = $category->title;
}
Nous avons un statut 204 no content, mais si nous regardons en base de données, nous avons bien le changement de titre et de slug.
Le verbe DELETE
Rien d’extraordinaire ici, nous continuons sur notre lancée:
/**
* @Rest\Delete(
* path = "/{id}",
* name = "app_api_delete_category",
* requirements = {"id"="\d+"}
* )
*
* @Rest\View(statusCode=204)
*
* @Doc\ApiDoc(
* section="Categories",
* description="Delete an existing category.",
* statusCodes={
* 201="Returned if category has been successfully deleted",
* 400="Returned if category does not exist",
* 500="Returned if server error"
* },
* requirements={
* {
* "name"="id",
* "dataType"="integer",
* "requirement"="\d+",
* "description"="The category unique identifier."
* }
* },
* )
*/
public function deleteCategoryAction(Category $category)
{
$this->get('app_core.category_manager')->remove($category);
return $this->view('', Response::HTTP_NO_CONTENT);
}
Et nous rajoutons cette fonction dans notre manager:
public function remove(Category $category)
{
$this->em->remove($category);
$this->em->flush();
}
Dans le prochain article, nous verrons comment customiser le rendu xml, faire des requêtes avec les paramsFetcher, paginer les résultats…
Le code source se trouve ici: https://github.com/jpsymfony/REST-BEHAT





Commentaires