I’m trying to create a getCollection route using POST with API Platform. The idea is to perform a search that takes a JSON payload to handle search conditions first, and later manage relations, sorting, etc.
I don’t want to use GET, because I might run into URL length limitations, and I also want to be able to easily work with it in tools like Postman, for example.
I’ve already tried the following:
https://api-platform.com/docs/symfony/controllers/
API Platform and custom POST operation with custom body (outdated)
https://github.com/api-platform/api-platform/issues/1976 (not hydrating)
Here’s my code:
<?php
namespace App\ApiResource\V1;
use ApiPlatform\Metadata\ApiResource;
use App\Api\Utils\ApiResourceInterface;
use App\Api\V1\State\Courses\Chapter\ChapterSearchDataProvider as ChapterChapterSearchDataProvider;
use App\Entity\Chapter as EntityChapter;
use App\Utils\Api\Operations\SearchCollection;
use DateTime;
#[ApiResource(
routePrefix: '/v1',
operations: [
new SearchCollection(
uriTemplate: '/chapters/search',
name: 'chapter_search',
provider: ChapterChapterSearchDataProvider::class,
input: false,
output: Chapter::class,
read: false,
write: false,
deserialize: false,
status: 200
),
]
)]
class Chapter implements ApiResourceInterface
{
public string $id;
public string $course;
public ?string $previousChapter;
public ?string $nextChapter;
public string $title;
public DateTime $createdAt;
public DateTime $updatedAt;
public function __construct(
string $id,
string $course,
?string $previousChapter,
?string $nextChapter,
string $title,
DateTime $createdAt,
DateTime $updatedAt
) {
$this->id = $id;
$this->course = $course;
$this->previousChapter = $previousChapter;
$this->nextChapter = $nextChapter;
$this->title = $title;
$this->createdAt = $createdAt;
$this->updatedAt = $updatedAt;
}
public static function getEntityClass() : string {
return EntityChapter::class;
}
}
class SearchCollection extends HttpOperation implements CollectionOperationInterface
{
public function __construct(
// ...
) {
parent::__construct(
method: 'POST',
// ...
);
}
<?php
namespace App\Api\V1\State\Courses\Chapter;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Api\V1\Mapper\Courses\Chapter\ChapterMapper;
use App\ApiResource\V1\Chapter;
use App\Repository\ChapterRepository;
final class ChapterSearchDataProvider implements ProviderInterface
{
private ChapterRepository $repository;
private ChapterMapper $mapper;
public function __construct(
ChapterRepository $repository,
ChapterMapper $mapper
) {
$this->repository = $repository;
$this->mapper = $mapper;
}
/**
* Provides data.
*
* @param array<string, mixed> $uriVariables
* @param array<string, mixed>|array{request?: Request, resource_class?: string} $context
*
* @return T|PartialPaginatorInterface<T>|iterable<T>|null
*/
public function provide(
Operation $operation,
array $uriVariables = [],
array $context = []
): object|array|null {
$chapters = $this->repository->findAll();
return $this->mapper->fromArray($chapters);
}
}