Ajouter un message a une discussion

Sur notre entité Discussion rajoutons une nouvelle route

/**
 * @ApiResource(
 *     collectionOperations={
 *          "post"={
 *                "input"=CreateDiscussionInput::class,
 *                "denormalization_context"={"groups"={"discussion:write"}},
 *                "output"=CreateDiscussionOutput::class
 *          },
 *          "createNewMessage"={
 *                "method"="POST",
 *                "path"="discussions/{id}/messages",
 *                "input"=CreateMessageInput::class,
 *                "denormalization_context"={"groups"={"message:write"}},
 *           },
 *          "get"
 *    },
 *    normalizationContext={"groups"={"discussion:read"}},
 * )
 * @ORM\Entity(repositoryClass=DiscussionRepository::class)
 */
class Discussion

Nous créons le DTO input CreateMessageInput

<?php

// src/DTO/CreateMessageInput.php

namespace App\DTO;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;

class CreateMessageInput
{
    /**
     * @Assert\NotBlank()
     * @Groups({"message:write"})
     */
    public $contentMessage;
}

Son DataTransformer

<?php

// src/DataTransformer/CreateMessageInputDataTransformer.php

namespace App\DataTransformer;

use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use App\Entity\Discussion;
use App\Entity\Message;

class CreateMessageInputDataTransformer implements DataTransformerInterface
{
    /**
     * {@inheritdoc}
     */
    public function transform($data, string $to, array $context = [])
    {
        $message = new Message();
        $message->setContent($data->contentMessage);

        return $message;
    }

    /**
     * {@inheritdoc}
     */
    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        // in the case of an input, the value given here is an array (the JSON decoded).
        if ($data instanceof Discussion) {
            return false;
        }

        return (Discussion::class === $to)
            && null !== ($context['input']['class'] ?? null)
            && (array_key_exists('collection_operation_name', $context))
            && $context['collection_operation_name'] === 'createNewMessage';;
    }
}

Et enfin notre DataPersister. Notez qu'on regarde si on est sur une instance Message alors que dans notre DataTransformer on regardait si on était sur Discussion. C'est parce que notre DataTransformer retourne une nouvelle instance de Message.

Dans notre DataPersister on récupère la discussion car on connait son id avec le path "path"="discussions/{id}/messages"

<?php

// src/DataPersister/CreateMessageInputDataPersister.php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Message;
use App\Repository\DiscussionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class CreateMessageInputDataPersister implements ContextAwareDataPersisterInterface
{
    /**
     * @var RequestStack
     */
    private $requestStack;
    /**
     * @var DiscussionRepository
     */
    private $discussionRepository;
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(
        RequestStack $requestStack,
        DiscussionRepository $discussionRepository,
        EntityManagerInterface $entityManager
    )
    {
        $this->requestStack = $requestStack;
        $this->discussionRepository = $discussionRepository;
        $this->entityManager = $entityManager;
    }

    public function supports($data, array $context = []): bool
    {
        return $data instanceof Message
            && (array_key_exists('collection_operation_name', $context)
            && $context['collection_operation_name'] === 'createNewMessage');
    }

    /**
     * @param Message $data
     * @param array $context
     * @return void
     */
    public function persist($data, array $context = [])
    {
        $id = $this->requestStack->getMasterRequest()->attributes->get('id');
        $discusssion = $this->discussionRepository->find($id);

        $data->setDiscussion($discusssion);

        $this->entityManager->persist($data);
        $this->entityManager->flush();

        return $data;
    }

    public function remove($data, array $context = [])
    {
    }
}

A cet instant, si on fait un POST /api/discussions/{id}/messages avec comme payload

{
    "contentMessage": "Facilis nisi mollitia ut necessitatibus explicabo veniam. Eligendi ratione sunt voluptas est consequatur expedita fuga ut. Officia necessitatibus maiores cumque."
}

On à comme réponse [] car notre entité Message n'est pas déclaré comme une ressource ApiPlatform, voici les modifications à apporter :

use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\MessageRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass=MessageRepository::class)
 */
class Message

Et à l'attribut content nous rajoutons le groupe discussion:read car nous arrivons de cette entité.

/**
 * @ORM\Column(type="text")
 * @Groups({"discussion:read"})
 */
private $content;

Maintenant en refaisant un POST /api/discussions/{id}/messages avec comme payload

{
    "contentMessage": "Facilis nisi mollitia ut necessitatibus explicabo veniam. Eligendi ratione sunt voluptas est consequatur expedita fuga ut. Officia necessitatibus maiores cumque."
}

nous avons la réponse :

{
    "@context": "/api/contexts/Message",
    "@id": "/api/messages/92",
    "@type": "Message",
    "content": "Facilis nisi mollitia ut necessitatibus explicabo veniam. Eligendi ratione sunt voluptas est consequatur expedita fuga ut. Officia necessitatibus maiores cumque."
}
prev next

Commentaires

Connectez-vous pour laisser un commentaire