Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

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. Группы решают это сразу: в ответ попадают только отмеченные свойства.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.