API и JSON
API отдаёт не страницы, а данные в формате JSON — для мобильного приложения или интерфейса на JavaScript. В Symfony для этого есть метод json() и сериализатор, который превращает объекты в JSON и обратно.
Ответ JSON
#[Route('/api/posts', name: 'api_posts', methods: ['GET'])]
public function index(PostRepository $posts): JsonResponse
{
return $this->json($posts->findAll());
}json() передаёт объекты сериализатору: он читает свойства через методы get и собирает JSON.
Что попадает в ответ
Без ограничений в ответ попадут все свойства, включая лишние и секретные. Набор полей задают группами:
use Symfony\Component\Serializer\Attribute\Groups;
class Post
{
#[Groups(['post:list', 'post:read'])]
private ?int $id = null;
#[Groups(['post:list', 'post:read'])]
private ?string $title = null;
#[Groups(['post:read'])]
private ?string $body = null;
}return $this->json($posts->findAll(), context: ['groups' => ['post:list']]);В списке будут только id и title, а на странице одной статьи с группой post:read — ещё и текст.
Одна запись
#[Route('/api/posts/{id}', name: 'api_post', methods: ['GET'])]
public function show(Post $post): JsonResponse
{
return $this->json($post, context: ['groups' => ['post:read']]);
}Приём данных
Присланный JSON удобно превращать в отдельный объект с правилами проверки:
// src/Dto/PostInput.php
namespace App\Dto;
use Symfony\Component\Validator\Constraints as Assert;
class PostInput
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Length(min: 5)]
public string $title,
#[Assert\NotBlank]
public string $body,
) {}
}Атрибут #[MapRequestPayload] заполняет его и проверяет:
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
#[Route('/api/posts', name: 'api_post_create', methods: ['POST'])]
public function create(
#[MapRequestPayload] PostInput $input,
EntityManagerInterface $em,
): JsonResponse {
$post = new Post();
$post->setTitle($input->title);
$post->setBody($input->body);
$em->persist($post);
$em->flush();
return $this->json($post, 201, context: ['groups' => ['post:read']]);
}Если данные не прошли проверку, Symfony сам вернёт ответ с кодом 422 и списком ошибок.
Коды состояния
| Код | Когда возвращать |
|---|---|
200 | Успех |
201 | Запись создана |
204 | Успех без содержимого, например после удаления |
404 | Запись не найдена |
422 | Данные не прошли проверку |
return new JsonResponse(null, 204);API Platform
Для большого API существует API Platform — надстройка над Symfony. Достаточно пометить сущность атрибутом #[ApiResource], и появятся все адреса, постраничный вывод, фильтры и документация.
Частая ошибка
Связанные сущности ссылаются друг на друга: статья — на автора, автор — на свои статьи. Без групп сериализатор пойдёт по кругу и завершится ошибкой A circular reference has been detected. Группы решают это сразу: в ответ попадают только отмеченные свойства.