Новое в Symfony 6.3 — Сопоставление данных Request с типизирова­н­ны­ми объектами

Источник: «New in Symfony 6.3: Mapping Request Data to Typed Objects»
В Symfony 6.3 представлены два новых атрибута PHP для сопоставления данных входящего запроса Request с типизированными объектами, таким как DTO, и их автоматическая проверка.

Повторяющийся запрос функций Symfony в течении последних нескольких лет заключался в сопоставлении данных входящего запроса в типизированные объекты, такие как DTO (объекты передачи данных). В Symfony 6.3, мы, наконец, вводим несколько новых атрибутов для сопоставления запросов с типизированными объектами и их проверки.

Во-первых, атрибут #[MapRequestPayload] берёт данные из PHP суперглобальной переменной $_POST (через метод $request->request->all() объекта Symfony Request) и пытается заполнить им заданный типизированный объект.

Рассмотрим следующий класс DTO:

// ...
use Symfony\Component\Validator\Constraints as Assert;

class ProductReviewDto
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Length(min: 10, max: 500)]
public readonly string $comment,

#[Assert\GreaterThanOrEqual(1)]
#[Assert\LessThanOrEqual(5)]
public readonly int $rating,
) {
}
}

В Symfony 6.3 используйте этот класс как подсказку типа для аргумента контроллера и примените атрибут #[MapRequestPayload]. Symfony автоматически сопоставит данные запроса с объектом DTO и проверит их:

// ...
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;

class ProductApiController
{
public function __invoke(
#[MapRequestPayload] ProductReviewDto $productReview,
): Response {

// here, $productReview is a fully typed representation of the request data

}
}

Вот и всё. О возможных условиях ошибки при сопоставлении данных:

Аналогично, #[MapQueryString] берёт данные из PHP суперглобальной переменной $_GET (через метод $request->request->all() объекта Symfony Request) и пытается заполнить им заданный типизированный объект.

Рассмотрим следующий набор классов DTO:

// ...
use Symfony\Component\Validator\Constraints as Assert;

class OrdersQueryDto
{
public function __construct(
#[Assert\Valid]
public readonly ?OrdersFilterDto $filter,

#[Assert\LessThanOrEqual(500)]
public readonly int $limit = 25,

#[Assert\LessThanOrEqual(10_000)]
public readonly int $offset = 0,
) {
}
}

class OrdersFilterDto
{
public function __construct(
#[Assert\Choice(['placed', 'shipped', 'delivered'])]
public readonly ?string $status,

public readonly ?float $total,
) {
}
}

В Symfony 6.3 используйте этот класс как подсказку типа для аргумента контроллера и примените атрибут #[MapQueryString]. Symfony автоматически сопоставит данные запроса с объектом DTO и проверит их:

// ...
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;

class SearchApiController
{
public function __invoke(
#[MapQueryString] OrdersQueryDto $query,
): Response {

// here, $query is a fully typed representation of the request data

}
}

Логика валидации и условия ошибки этого атрибута такие же, как и раньше. Кроме того, в обоих случаях вы можете настроить как контекст, так и класс, используемый для сопоставления запроса с вашими объектами:

#[MapRequestPayload(
context: ['...'],
resolver: App\...\ProductReviewRequestValueResolver
)
]

ProductReviewDto $productReview

#[MapQueryString(
context: ['...'],
resolver: App\...\OrderSearchRequestValueResolver
)
]

OrdersQueryDto $query

Дополнительные материалы

Предыдущая Статья

Продвинутый Laravel: Контракты и Реализации

Следующая Статья

Архитектурный плагин Pest