skip to Main Content

I have a symfony API endpoint that goes like this:

/orders/list-all?since=2024-10-21

I’d like to map that query string parameter to a DateTimeImmutable (or DateTimeInterface), like so:

    public function listOrders(
        #[OAQueryParameter(name: 'since', in: 'query', description: 'Date de début de la recherche', required: false, schema: new OASchema(type: 'string', format: 'date'))]
        #[MapQueryParameter()]
        ?DateTimeInterface $since = null
    ): Response

But this doesn’t seems to work.
I’ve looked at :

  • DateTimeValueResolver
  • MapDateTime (that would allow me to specify the date format)

But I don’t get how they are supposed to work, and the documentation is very light on that matter.

By reading and debugging though the DateTimeValueResolver source code, I’ve seen that $request->attributes->has($argument->getName()) is always false.

Is there any example usage out there ?

2

Answers


  1. You cannot use MapQueryParameter with types other than array, string, int, float, bool or BackedEnum.

    What you can do is to accept since as type string and then create a DateTimeImmutable afterward.

    use SymfonyBundleFrameworkBundleControllerAbstractController;
    use SymfonyComponentHttpKernelAttributeMapQueryParameter;
    use SymfonyComponentRoutingAttributeRoute;
    
    final class TestController extends AbstractController
    {
        #[Route(path: '/list', name: 'get-list')]
        public function getListAction(
            #[MapQueryParameter]
            ?string $since = null,
        )
        {
            $date = DateTimeImmutable::createFromFormat('Y-m-d', $since);
            dd($date);
        }
    }
    

    The MapDateTime takes the data from $request->attributes and not from $request->query, where the since parameter is in.

    So for now, I think you need to stick with the above mentioned logic.

    Login or Signup to reply.
  2. In addition to answer above, you can solve this issue by using the #[MapQueryString] and mapping your parameters into a DTO. This could prove useful if you plan on adding more parameters to filter by. Here is a minimal example of what I mean:

    Controller:

    <?php
    
    namespace AppController;
    
    use SymfonyBundleFrameworkBundleControllerAbstractController;
    use SymfonyComponentHttpFoundationResponse;
    use SymfonyComponentHttpKernelAttributeMapQueryString;
    use SymfonyComponentRoutingAttributeRoute;
    
    class TestController extends AbstractController
    {
    class TestController extends AbstractController
    {
        #[Route('/test', name: 'app_test')]
        public function index(#[MapQueryString] TestReq $req = new TestReq()): never
        {
            dd($req);
        }
    }
    }
    

    DTO:

    <?php
    
    namespace AppController;
    
    class TestReq
    {
        public function __construct(
            public readonly ?DateTimeInterface $from = null,
            public readonly ?DateTimeInterface $to = null,
        ) {
        }
    }
    

    You can also add the OpenAPI annotations or Validator validation into your DTO. I also provide the output produced for this solution in screenshots from Postman:

    Without params:
    sample request without query parameters

    With $from param:
    sample request with query parameter

    Good thing is, that you can combine #[MapQueryString] attributes into different DTOs. Good example may be pagination of the list:

        #[Route('/test', name: 'app_test')]
        public function index(
            #[MapQueryString] TestReq $req = new TestReq(),
            #[MapQueryString] Pagination $pnt = new Pagination(),
        ): never
        {
            dd($req, $pnt);
        }
    
    class Pagination
    {
        public function __construct(
            public readonly int $page = 1,
            public readonly int $pageSize = 25,
        ) {
        }
    }
    

    Sample request:
    demo of multiple mapquerystrings

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search