API Platform із Symfony у 2026: Архітектура, State Providers та питання на співбесіді
Опануйте API Platform 4.2 із Symfony: State Providers, Processors, Object Mapper, JSON Streamer та оптимізації продуктивності. Найпоширеніші питання на співбесіді для досвідчених розробників.

API Platform 4.2 трансформує спосіб, яким Symfony-застосунки надають REST та GraphQL API. Ця версія представляє Symfony Object Mapper для чіткого розділення ресурсів, JSON Streamer для значного підвищення продуктивності та перероблену систему фільтрів. Для розробників, які готуються до технічних співбесід, розуміння цих архітектурних патернів відрізняє senior-кандидатів від junior.
API Platform 4.2 потребує Symfony 7.4 або 8.0. Підтримку Symfony 6.4 та 7.0-7.3 припинено. JSON Streamer забезпечує до 32% більше запитів на секунду на endpoint'ах колекцій.
Налаштування API Platform 4.2 із Symfony
API Platform встановлюється через Symfony Flex з автоматичним налаштуванням. Стандартне налаштування охоплює більшість випадків використання, залишаючись повністю конфігурованим для складних доменних вимог.
# Встановлення API Platform
composer require api-platform/symfony
# Документація API доступна за адресою /api/
# Відкрийте http://localhost:8000/api/ після запуску сервера
symfony serveРецепт Flex налаштовує групи серіалізації, інтеграцію з Doctrine та генерацію документації OpenAPI. API-ресурси надають CRUD-операції шляхом додавання одного атрибута до класів сутностей.
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use ApiPlatform\Metadata\Delete;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource(
operations: [
new GetCollection(),
new Get(),
new Post(security: "is_granted('ROLE_ADMIN')"),
new Put(security: "is_granted('ROLE_ADMIN')"),
new Delete(security: "is_granted('ROLE_ADMIN')")
],
normalizationContext: ['groups' => ['book:read']],
denormalizationContext: ['groups' => ['book:write']]
)]
class Book
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Groups(['book:read', 'book:write'])]
private string $title;
#[ORM\Column(type: 'text')]
#[Groups(['book:read', 'book:write'])]
private string $description;
#[ORM\Column]
#[Groups(['book:read'])]
private \DateTimeImmutable $createdAt;
// Геттери та сеттери...
}Ця конфігурація генерує п'ять endpoint'ів з автоматичною валідацією, серіалізацією та документацією OpenAPI.
State Providers: Отримання даних з будь-якого джерела
State Providers контролюють спосіб, яким API Platform отримує дані для GET-операцій. Стандартний Doctrine provider обробляє отримання сутностей, але власні провайдери дозволяють інтеграцію із зовнішніми API, Elasticsearch або кешованими даними.
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Repository\BookRepository;
use Psr\Cache\CacheItemPoolInterface;
final class BookStateProvider implements ProviderInterface
{
public function __construct(
private BookRepository $repository,
private CacheItemPoolInterface $cache
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
// Отримання одиничного елемента
if (isset($uriVariables['id'])) {
$cacheKey = sprintf('book_%d', $uriVariables['id']);
$item = $this->cache->getItem($cacheKey);
if ($item->isHit()) {
return $item->get();
}
$book = $this->repository->find($uriVariables['id']);
$item->set($book)->expiresAfter(3600);
$this->cache->save($item);
return $book;
}
// Отримання колекції з власною фільтрацією
return $this->repository->findActiveBooks();
}
}Реєстрація провайдера для конкретних операцій:
#[ApiResource(
operations: [
new GetCollection(provider: BookStateProvider::class),
new Get(provider: BookStateProvider::class),
// Інші операції використовують стандартний Doctrine provider
new Post(),
new Put(),
]
)]
class Book { /* ... */ }State Processors: Обробка мутацій з бізнес-логікою
State Processors обробляють операції POST, PUT, PATCH та DELETE. Вони отримують десеріалізовані дані та застосовують бізнес-логіку перед збереженням.
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProcessorInterface;
use App\Entity\Book;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
final class BookStateProcessor implements ProcessorInterface
{
public function __construct(
private EntityManagerInterface $em,
private MailerInterface $mailer,
private ProcessorInterface $persistProcessor
) {}
public function process(
mixed $data,
Operation $operation,
array $uriVariables = [],
array $context = []
): mixed {
// Бізнес-логіка перед збереженням
if ($data instanceof Book && $operation instanceof Post) {
$data->setCreatedAt(new \DateTimeImmutable());
$data->setSlug($this->generateSlug($data->getTitle()));
}
// Делегування до Doctrine processor
$result = $this->persistProcessor->process($data, $operation, $uriVariables, $context);
// Сповіщення після збереження
if ($operation instanceof Post) {
$this->notifyNewBook($data);
}
return $result;
}
private function generateSlug(string $title): string
{
return strtolower(preg_replace('/[^a-zA-Z0-9]+/', '-', $title));
}
private function notifyNewBook(Book $book): void
{
$email = (new Email())
->to('catalog@example.com')
->subject('Нова книга додана: ' . $book->getTitle())
->text('Нову книгу було додано до каталогу.');
$this->mailer->send($email);
}
}Декорування стандартного Doctrine processor за допомогою #[AsDecorator] зберігає поведінку збереження, додаючи при цьому власну логіку. Цей патерн запобігає дублюванню ORM-операцій.
Object Mapper: Відокремлення API-ресурсів від сутностей
API Platform 4.2 інтегрує компонент Symfony Object Mapper для відокремлення API-представлень від доменних сутностей. Це розділення дозволяє різні моделі читання/запису та захищає внутрішні структури сутностей.
namespace App\ApiResource;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use App\Entity\Book;
use Symfony\Component\ObjectMapper\Attribute\Map;
#[ApiResource(
shortName: 'Book',
operations: [
new GetCollection(),
new Get()
]
)]
#[Map(target: Book::class)]
class BookResource
{
public ?int $id = null;
public string $title;
public string $description;
// Обчислюване поле, відсутнє в сутності
public int $wordCount;
// Форматована дата для споживачів API
public string $publishedDate;
}Mapper provider автоматично трансформує сутності в ресурси:
namespace App\State;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\ApiResource\BookResource;
use App\Repository\BookRepository;
use Symfony\Component\ObjectMapper\ObjectMapperInterface;
final class BookResourceProvider implements ProviderInterface
{
public function __construct(
private BookRepository $repository,
private ObjectMapperInterface $mapper
) {}
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (isset($uriVariables['id'])) {
$book = $this->repository->find($uriVariables['id']);
return $book ? $this->toResource($book) : null;
}
return array_map(
fn(Book $book) => $this->toResource($book),
$this->repository->findAll()
);
}
private function toResource(Book $book): BookResource
{
$resource = $this->mapper->map($book, BookResource::class);
$resource->wordCount = str_word_count($book->getDescription());
$resource->publishedDate = $book->getCreatedAt()->format('j F Y');
return $resource;
}
}Готовий до співбесід з Symfony?
Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.
JSON Streamer: 32% підвищення продуктивності
Компонент JSON Streamer серіалізує великі колекції без завантаження всіх наборів даних у пам'ять. Бенчмарки на Sylius API показали 32,4% збільшення кількості запитів на секунду.
Увімкнення потокового передавання на рівні ресурсу або операції:
#[ApiResource(
operations: [
new GetCollection(
jsonStream: true, // Увімкнути JSON streaming
paginationItemsPerPage: 100
),
new Get()
]
)]
class Book { /* ... */ }Потокове передавання особливо корисне для:
- Endpoint'ів колекцій з понад 50 елементами
- Ресурсів із вкладеними зв'язками
- API, що обслуговують мобільних клієнтів з обмеженою пропускною здатністю
Специфікацію OpenAPI також оптимізовано. Мутуалізація JSON Schema зменшує розмір файлу на 30%, покращуючи час завантаження документації.
Питання на співбесіді: Архітектура API Platform
Технічні співбесіди на позиції Symfony часто охоплюють патерни API Platform. Ці питання оцінюють розуміння архітектури фреймворку за межами базових CRUD-операцій.
"Поясніть різницю між State Providers та Processors"
Очікувана відповідь: State Providers отримують дані для операцій читання (GET). Вони повертають сутності, DTO або масиви. State Processors обробляють операції запису (POST, PUT, PATCH, DELETE). Вони отримують десеріалізований ввід та виконують бізнес-логіку перед збереженням. Це розділення слідує принципам CQRS: запити через Providers, команди через Processors.
"Коли використовувати власний API Resource замість прямого надання сутності?"
Очікувана відповідь: Власні ресурси застосовуються, коли:
- API-представлення відрізняється від схеми бази даних
- Обчислювані поля потребують агрегації з кількох сутностей
- Моделі запису та читання потребують різних структур
- Внутрішні поля сутності повинні залишатися прихованими від споживачів API
- Сумісність версій потребує стабільних контрактів під час еволюції сутностей
"Як API Platform обробляє валідацію?"
Очікувана відповідь: API Platform використовує constraint'и Symfony Validator на властивостях сутностей. Валідація запускається автоматично під час десеріалізації перед виконанням State Processor. Групи валідації контролюють, які constraint'и застосовуються для кожної операції. Власні валідатори інтегруються через стандартні механізми Symfony.
#[ApiResource(
operations: [
new Post(validationContext: ['groups' => ['create']]),
new Put(validationContext: ['groups' => ['update']])
]
)]
class Book
{
#[Assert\NotBlank(groups: ['create', 'update'])]
private string $title;
#[Assert\Isbn(groups: ['create'])]
private string $isbn; // Потрібно лише при створенні
}Кандидати часто описують валідацію як "автоматичну" без згадки про групи валідації або власні constraint'и. Інтерв'юери шукають розуміння того, як налаштовувати валідацію для кожної операції.
"Які механізми безпеки надає API Platform?"
Очікувана відповідь: API Platform інтегрується з Symfony Security через:
- Атрибут
securityна операціях для доступу на основі ролей securityPostDenormalizeдля перевірок на рівні об'єкта після прив'язки даних- Voter'и для складної логіки авторизації
- Rate limiting через інтеграцію з Symfony Rate Limiter
#[ApiResource(
operations: [
new Get(
security: "is_granted('ROLE_USER')"
),
new Put(
security: "is_granted('ROLE_ADMIN') or object.getOwner() == user",
securityPostDenormalize: "is_granted('BOOK_EDIT', object)"
)
]
)]Фільтри та пагінація: Розширені патерни запитів
Фільтри API Platform дозволяють клієнтам запитувати колекції за допомогою URL-параметрів. Систему фільтрів у версії 4.2 перероблено для кращої розширюваності.
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
'title' => 'partial', // LIKE %value%
'author.name' => 'exact' // Вкладена властивість
])]
#[ApiFilter(DateFilter::class, properties: ['createdAt'])]
#[ApiFilter(OrderFilter::class, properties: ['title', 'createdAt'])]
class Book { /* ... */ }Згенеровані endpoint'и:
GET /api/books?title=symfony # Пошук за назвою
GET /api/books?createdAt[after]=2026-01-01 # Діапазон дат
GET /api/books?order[createdAt]=desc # СортуванняТестування ресурсів API Platform
API Platform надає тестовий клієнт, який спрощує функціональні тести. Клас ApiTestCase пропонує assertion'и, специфічні для відповідей API.
namespace App\Tests\Api;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
use App\Entity\Book;
class BookTest extends ApiTestCase
{
public function testGetCollection(): void
{
$response = static::createClient()->request('GET', '/api/books');
$this->assertResponseIsSuccessful();
$this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
$this->assertJsonContains([
'@context' => '/api/contexts/Book',
'@type' => 'Collection'
]);
}
public function testCreateBook(): void
{
$response = static::createClient()->request('POST', '/api/books', [
'json' => [
'title' => 'Найкращі практики Symfony',
'description' => 'Посібник із сучасної розробки на Symfony'
],
'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
]);
$this->assertResponseStatusCodeSame(201);
$this->assertJsonContains(['title' => 'Найкращі практики Symfony']);
}
public function testCreateBookValidationFails(): void
{
$response = static::createClient()->request('POST', '/api/books', [
'json' => ['description' => 'Відсутня назва'],
'headers' => ['Authorization' => 'Bearer ' . $this->getToken()]
]);
$this->assertResponseStatusCodeSame(422);
$this->assertJsonContains([
'violations' => [
['propertyPath' => 'title', 'message' => 'This value should not be blank.']
]
]);
}
}Ключові висновки для API Platform із Symfony
- State Providers обробляють GET-операції, State Processors обробляють мутації. Це розділення дозволяє чисту архітектуру з власними джерелами даних та бізнес-логікою
- Компонент Object Mapper відокремлює API-ресурси від Doctrine-сутностей, дозволяючи різні моделі читання/запису та захищаючи внутрішні структури
- JSON Streamer забезпечує 32% підвищення продуктивності на endpoint'ах колекцій шляхом серіалізації без повного виділення пам'яті
- Безпека інтегрується через стандартні механізми Symfony: вирази
security, Voter'и та компонент Rate Limiter - Групи валідації налаштовують застосування constraint'ів для кожної операції
- Фільтри автоматично надають параметри запитів, причому фільтри пошуку, дати та порядку охоплюють більшість випадків використання
- Питання на співбесіді зосереджуються на архітектурних рішеннях: коли використовувати власні провайдери, як розділяти моделі читання/запису та патерни реалізації безпеки
Починай практикувати!
Перевір свої знання з нашими симуляторами співбесід та технічними тестами.
Чи знайдеш ти помилку в Symfony?
Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Автор:
Anthony Fillion-MailletЗасновник SharpSkill
Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.
Оновлено 8 вересня 2026 р.
Теги
Поділитися
Пов'язані статті

API Platform Symfony REST: Повний посібник та питання на співбесіді 2026
Створення продакшн-готових REST API з API Platform 4 та Symfony 7. State Providers, Processors, фільтри та найпоширеніші питання на співбесіді про API Platform.

Symfony 8 у 2026 році: нові можливості, PHP 8.4 Lazy Objects та питання для співбесід
Symfony 8 new features: нативні lazy objects PHP 8.4, багатокрокові форми, invokable-команди, JSON Streamer та питання для технічних співбесід 2026.

API Platform GraphQL Symfony: Схеми, Мутації та Питання на Співбесіді 2026
Повний посібник з інтеграції GraphQL з API Platform у Symfony. Схеми, запити, мутації, резолвери, безпека та питання на технічну співбесіду.