DEV Community

Aymeric Ratinaud
Aymeric Ratinaud

Posted on

Create a new Discussion with a Message and a Message to an existing Discussion [Api-platform]

Introduction

We will create two POST routes, one to create a Discussion with a Message and a second to add a Message to that Discussion. Both POSTs will have the same request body.

The interest of this article is to show you how to define your entity to wait for a Message (while it is a OneToMany relationship and should therefore wait for an array of messages).

Then we will create a route that will indicate the Discussion on which we will add a new Message

Create our Message entity

 <?php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Post; use App\Repository\MessageRepository; use App\State\MessagePostProcessor; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: MessageRepository::class)] #[ApiResource( normalizationContext: [ 'groups' => ['message:read'] ], denormalizationContext: [ 'groups' => ['message:write'] ], )] class Message { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(type: Types::TEXT)] #[Groups(groups: ['message:read', 'message:write'])] #[Assert\NotBlank()] private ?string $content = null; #[ORM\ManyToOne(inversedBy: 'messages')] #[ORM\JoinColumn(nullable: false)] private ?Discussion $discussion = null; public function getId(): ?int { return $this->id; } public function getContent(): ?string { return $this->content; } public function setContent(string $content): self { $this->content = $content; return $this; } public function getDiscussion(): ?Discussion { return $this->discussion; } public function setDiscussion(?Discussion $discussion): self { $this->discussion = $discussion; return $this; } } 
Enter fullscreen mode Exit fullscreen mode

Our Discussion entity

<?php namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\DiscussionRepository; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: DiscussionRepository::class)] #[ApiResource( normalizationContext: [ 'groups' => ['discussion:read'] ], denormalizationContext: [ 'groups' => ['discussion:write'] ], )] class Discussion { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\OneToMany(mappedBy: 'discussion', targetEntity: Message::class, cascade: ['persist'], orphanRemoval: true)] private Collection $messages; public function __construct() { $this->messages = new ArrayCollection(); } public function getId(): ?int { return $this->id; } /** * @return Collection<int, Message> */ public function getMessages(): Collection { return $this->messages; } public function addMessage(Message $message): self { if (!$this->messages->contains($message)) { $this->messages->add($message); $message->setDiscussion($this); } return $this; } public function removeMessage(Message $message): self { if ($this->messages->removeElement($message)) { // set the owning side to null (unless already changed) if ($message->getDiscussion() === $this) { $message->setDiscussion(null); } } return $this; } } 
Enter fullscreen mode Exit fullscreen mode

First part - Create a new Discussion with a Message

Now we would like that on the POST /api/discussions we can send the content of the message this way:

{ "content": "Fuga ducimus debitis fuga quis sint similique dolores." } 
Enter fullscreen mode Exit fullscreen mode

and not in this way

{ "messages": [ "content": "Fuga ducimus debitis fuga quis sint similique dolores." ], } 
Enter fullscreen mode Exit fullscreen mode

We will create an attribute on Discussion that will not be persisted.

#[Groups(groups: ['discussion:write'])] private string $content; 
Enter fullscreen mode Exit fullscreen mode

With the discussion:write group, the setter setContent will be called and the message will be added with the method addMessage which is already present. This is the setter:

public function setContent(string $content): self { $message = new Message(); $message->setContent($content); $this->addMessage($message); return $this; } 
Enter fullscreen mode Exit fullscreen mode

Now we can make a POST

{ "content": "Fuga ducimus debitis fuga quis sint similique dolores." } 
Enter fullscreen mode Exit fullscreen mode

which create a Discussion and its related Message.

Second part - Create a new Message to an existing Discussion

Now we will define the following route, to retrieve the discussion and add a new message on it.

POST /api/discussions/{id}/message

{ "content": "Hic ut et excepturi molestias amet sit." } 
Enter fullscreen mode Exit fullscreen mode

To define the new route we are in the Message entity:

#[ORM\Entity(repositoryClass: MessageRepository::class)] #[ApiResource( normalizationContext: [ 'groups' => ['message:read'] ], denormalizationContext: [ 'groups' => ['message:write'] ], )] #[Post( uriTemplate: '/discussions/{id}/message', read: false, processor: MessagePostProcessor::class )] class Message { // ... 
Enter fullscreen mode Exit fullscreen mode

We need to create the MessagePostProcessor:

<?php namespace App\State; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; use App\Entity\Message; use App\Repository\DiscussionRepository; use Doctrine\ORM\EntityManagerInterface; class MessagePostProcessor implements ProcessorInterface { public function __construct( private readonly EntityManagerInterface $entityManager, private readonly DiscussionRepository $discussionRepository ) { } public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []) { $discussion = $this->discussionRepository->find($uriVariables['id']); $discussion->addMessage($data); $this->entityManager->persist($data); $this->entityManager->flush(); } } 
Enter fullscreen mode Exit fullscreen mode

This Processor gets the id of the Discussion with the variable uriVariables['id'] and then Message that is contained in $data is added to this discussion, and then persisted.

We can now make a POST to POST /api/discussions/{id}/message with the body

{ "content": "Hic ut et excepturi molestias amet sit." } 
Enter fullscreen mode Exit fullscreen mode

You can consult the implementation of this article on this commit: https://github.com/aratinau/api-platform3/commit/cc3cb8afb7331b07309565d66f169a106c92f349

🚀

Top comments (0)