Reputation: 969
I have an API Resource with 4 operations (Get, List, Post and Patch). Everything is working fine except for the Patch operation. It returns a 404 error with this Error log entry:
[2024-02-23T13:28:28.603322+00:00] app.ERROR: An exception occured, transforming to an Error resource. {"exception":"[object] (Symfony\Component\HttpKernel\Exception\NotFoundHttpException(code: 0): Not Found at /home/bbalet/studi/web-ecf/vendor/api-platform/core/src/State/Provider/ReadProvider.php:87)","operation":{"ApiPlatform\Metadata\Patch":[]}} []
My ApiIssue.php looks like this:
<?php
namespace App\ApiResource;
use App\State\IssueStateProvider;
use App\State\IssueStateProcessor;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Patch;
#[ApiResource(
shortName: 'Issue',
description: 'Issue Management. Restricted to employees.',
paginationEnabled: false,
operations: [
new GetCollection(
description: 'Get the list of issues for a room.',
uriTemplate: '/rooms/{id}/issues',
security: "is_granted('ROLE_EMPLOYEE')",
provider: IssueStateProvider::class
),
new Get(
description: 'Get an issue by its id.',
uriTemplate: '/issues/{id}',
security: "is_granted('ROLE_EMPLOYEE')",
provider: IssueStateProvider::class
),
new Post(
description: 'Create a new issue.',
security: "is_granted('ROLE_EMPLOYEE')",
uriTemplate: '/rooms/{id}/issues',
processor: IssueStateProcessor::class
),
new Patch(
description: 'Update an existing issue.',
security: "is_granted('ROLE_EMPLOYEE')",
uriTemplate: '/issues/{id}',
processor: IssueStateProcessor::class
)
]
)]
class ApiIssue
{
public ?int $id = null;
public ?string $title = null;
public ?string $status = null;
public ?string $description = null;
}
And my IssueStateProcessor.php like this:
<?php
namespace App\State;
use App\Repository\IssueRepository;
use App\Repository\RoomRepository;
use Doctrine\ORM\EntityManagerInterface;
use App\ApiResource\ApiIssue;
use App\Entity\Issue;
use ApiPlatform\Metadata\PostOperationInterface;
use ApiPlatform\Metadata\PatchOperationInterface;
use Psr\Log\LoggerInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class IssueStateProcessor implements ProcessorInterface
{
public function __construct(
private IssueRepository $issueRepository,
private RoomRepository $roomRepository,
private Security $security,
private EntityManagerInterface $entityManager,
private LoggerInterface $logger
) {}
/**
* {@inheritDoc}
*/
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
$user = $this->security->getUser();
if ($user === null) {
throw new AuthenticationException('Not authenticated or invalid token.');
}
if ($operation instanceof PostOperationInterface) {
$roomId = $uriVariables['id'];
$this->logger->info('Creating a new issue for room ' . $roomId);
$room = $this->roomRepository->findOneBy(['id' => $roomId]);
if (!$room) {
throw new NotFoundHttpException('Room not found.');
} else {
$issue = new Issue();
$issue->setUser($user);
$issue->setRoom($room);
$issue->setDate(new \DateTime());
$issue->setTitle($data->title);
$issue->setDescription($data->description);
$issue->setStatus(Issue::STATUS_NEW);
$this->entityManager->persist($issue);
$this->entityManager->flush();
//Return the updated issue from DB
$issueApi = new ApiIssue();
$issueApi->id = $issue->getId();
$issueApi->title = $issue->getTitle();
$issueApi->status = $issue->getStatusAsString();
$issueApi->description = $issue->getDescription();
return $issueApi;
}
} else if ($operation instanceof PatchOperationInterface) {
$issueId = $uriVariables['id'];
$this->logger->info('Updating issue ' . $issueId);
$issue = $this->issueRepository->findOneBy(['id' => $issueId]);
if (!$issue) {
throw new NotFoundHttpException('Issue not found.');
} else {
$issue->setTitle($data->title);
$issue->setDescription($data->description);
$issue->setStatus(intval($data->status));
$this->entityManager->persist($issue);
$this->entityManager->flush();
//Return the updated issue from DB
$issueApi = new ApiIssue();
$issueApi->id = $issue->getId();
$issueApi->title = $issue->getTitle();
$issueApi->status = $issue->getStatusAsString();
$issueApi->description = $issue->getDescription();
return $issueApi;
}
}
}
}
I'm not clear if the log entry says that it tries to read something from the StateProvider before giving it to the StateProcessor. And I don't know if I need a dedicated StateProcessor for each operation.
Upvotes: 1
Views: 300
Reputation: 303
Its because now Patch and Put operation also need a Provider, not only a Processor.
Upvotes: 0
Reputation: 969
The documentation of API Platform is misleading. The correct code is as below:
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Patch;
...
if ($operation instanceof Post) {
...
} else if ($operation instanceof Patch) {
...
Upvotes: 0