API Platform
Писать вручную контроллеры для каждой сущности — список, одна запись, создание, изменение, удаление — однообразная работа. API Platform создаёт такой API по сущностям Doctrine вместе с документацией, проверкой данных и постраничным выводом.
Установка
composer require apiРесурс
Достаточно пометить сущность атрибутом:
use ApiPlatform\Metadata\ApiResource;
#[ORM\Entity]
#[ApiResource]
class Post
{
// свойства и методы
}Появятся пять адресов:
| Метод и адрес | Действие |
|---|---|
GET /api/posts | Список |
POST /api/posts | Создание |
GET /api/posts/{id} | Одна запись |
PATCH /api/posts/{id} | Изменение |
DELETE /api/posts/{id} | Удаление |
По адресу /api откроется документация, где каждый запрос можно выполнить прямо в браузере.
Только нужные операции
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post as PostOperation;
#[ApiResource(operations: [
new GetCollection(),
new Get(),
new PostOperation(),
])]
class Post
{
// …
}Изменение и удаление через API стали недоступны.
Какие поля отдавать и принимать
Группы сериализации разделяют чтение и запись:
use Symfony\Component\Serializer\Attribute\Groups;
#[ApiResource(
normalizationContext: ['groups' => ['post:read']],
denormalizationContext: ['groups' => ['post:write']],
)]
class Post
{
#[Groups(['post:read'])]
private ?int $id = null;
#[Groups(['post:read', 'post:write'])]
#[Assert\NotBlank]
private ?string $title = null;
#[Groups(['post:read'])]
private ?\DateTimeImmutable $createdAt = null;
}Идентификатор и дату клиент видит, но изменить не может. Ограничения Assert проверяются автоматически: неверные данные дают ответ с кодом 422.
Постраничный вывод
#[ApiResource(paginationItemsPerPage: 20)]Клиент запрашивает страницы параметром ?page=2.
Фильтры
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: ['title' => 'partial', 'author' => 'exact'])]
#[ApiFilter(OrderFilter::class, properties: ['createdAt'])]
class Post
{
// …
}Теперь работают запросы /api/posts?title=php и /api/posts?order[createdAt]=desc.
Права доступа
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Patch;
#[ApiResource(operations: [
new GetCollection(),
new Get(),
new PostOperation(security: "is_granted('ROLE_USER')"),
new Patch(security: "object.getAuthor() == user"),
new Delete(security: "is_granted('ROLE_ADMIN')"),
])]В выражении object — запрошенная запись, user — текущий пользователь.
Своя логика
Действия при сохранении — назначить автора, отправить письмо — пишут в классе-обработчике (state processor), который подключают к операции параметром processor. Данные не из базы отдаёт поставщик (state provider).
Частая ошибка
Атрибут #[ApiResource] без настроек открывает все операции и все свойства сущности любому посетителю — включая удаление и поля вроде пароля. Для каждого ресурса задайте список операций, группы сериализации и правила security, а затем проверьте в документации /api, что видно гостю.