'\d+'])] #[IsGranted('IS_AUTHENTICATED_FULLY')] class MailLinkTaskController extends AbstractController { public function __construct( private readonly MailMessageRepository $messageRepository, private readonly TaskMailLinkRepository $linkRepository, private readonly EntityManagerInterface $em, private readonly MailAccessChecker $accessChecker, ) {} public function __invoke(Request $request, int $id): JsonResponse { $this->accessChecker->ensureCanAccessMail($this->getUser()); $message = $this->messageRepository->find($id); if (null === $message) { throw new NotFoundHttpException('Message not found'); } $body = json_decode($request->getContent(), true); $taskId = $body['taskId'] ?? null; if (null === $taskId) { throw new UnprocessableEntityHttpException('taskId is required'); } $task = $this->em->getRepository(Task::class)->find($taskId); if (null === $task) { throw new NotFoundHttpException('Task not found'); } $existing = $this->linkRepository->findByTaskAndMessage($task, $message); if (null !== $existing) { return $this->json(['message' => 'Already linked']); } $link = new TaskMailLink(); $link->setTask($task); $link->setMailMessage($message); $link->setLinkedAt(new DateTimeImmutable()); $link->setLinkedBy($this->getUser()); $this->em->persist($link); $this->em->flush(); return $this->json(['linkId' => $link->getId(), 'taskId' => $task->getId(), 'messageId' => $message->getId()], 201); } }