Новое в Symfony 6.3 — Сопоставление данных Request с типизированными объектами
Повторяющийся запрос функций 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
}
}
Вот и всё. О возможных условиях ошибки при сопоставлении данных:
- Ошибки валидации приведут к ответу со HTTP-статусом 422 (включая сериализованный объект
ConstraintViolationList
); - На искажённые данные вернётся ответ с ошибкой HTTP 400;
- Неподдерживаемые форматы десериализации вернут ответ с ошибкой HTTP 415.
Аналогично, #[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