ARTICLE AD BOX
I have a simple endpoint which has optional query parameters used for filtration. In my controller I use MapQueryString to map them to a DTO and validate inside. The problem is when I call the endpoint with no parameters at all I get 422 HTTP error by Symfony.
My controller endpoint:
#[SuccessfulPaginatedResponse(description: 'Returns paginated list of categories by given filters.')] #[ValidationErrorResponse] #[Route(name: 'categories_index', methods: ['GET'], format: 'json')] public function index( #[MapQueryString( validationGroups: ['default'], validationFailedStatusCode: Response::HTTP_UNPROCESSABLE_ENTITY )] IndexRequestQueryDto $queryParams, Request $request ): JSONResponse { $filter = CategoryFilterFactory::createFromIndexRequest($queryParams); $result = $this->categoryService->fetch($filter); $paginatedResponse = PaginatedResponse::create( $result['data'], $queryParams, $result['totalCount'], $request ); return $this->json($paginatedResponse->toArray()); }My DTO:
class IndexRequestQueryDto extends PaginatedRequest { public function __construct( #[Assert\Positive] #[Assert\Type('integer')] public readonly ?int $businessUnit = null, #[Assert\Positive] #[Assert\Type('integer')] public readonly ?int $parentId = null, #[Assert\Type('string')] public readonly ?string $name = null, ?int $page = null, ?int $pageSize = null, ) { parent::__construct($page, $pageSize); } }My DTO's extended class:
class PaginatedRequest implements DtoInterface { public const DEFAULT_PAGE = 1; public const DEFAULT_PAGE_SIZE = 25; public const MAX_PAGE_SIZE = 100; public function __construct( #[Assert\Type('integer')] #[Assert\Positive] public readonly ?int $page = null, #[Assert\Type('integer')] #[Assert\Positive] #[Assert\LessThanOrEqual(self::MAX_PAGE_SIZE)] public readonly ?int $pageSize = null, ) { }Things I tried which didn't work out:
- Removed all assertions
- In IndexRequestQueryDto tried to define page & pageSize without passing them to parent constructor
- Tried using #[Assert\When()]
The only thing that worked out for me was to use ValidatorInterface in my controller but that solution requires extra code and I don't like it:
$queryParams = new IndexRequestQueryDto( $request->query->has('businessUnit') ? (int) $request->query->get('businessUnit') : null, $request->query->has('parentId') ? (int) $request->query->get('parentId') : null, $request->query->has('name') ? (string) $request->query->get('name') : null, $request->query->has('page') ? (int) $request->query->get('page') : null, $request->query->has('pageSize') ? (int) $request->query->get('pageSize') : null, ); $errors = $this->validator->validate($queryParams); if (count($errors) > 0) { return $this->json(['errors' => $errors], Response::HTTP_UNPROCESSABLE_ENTITY); }Symfony version is 6.4, PHP is 8.2
