-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Injecting DTO into controller actions (DTOArgumentValueResolver) #36093
Comments
Hi @faizanakram99, we're discussing exactly the same thing on #36037. Take a look at this comment. |
Hi @tsantos84 Yep, I saw that RFC too. While your RFC has a very large scope, this RFC merely adds an I'd suggest to checkout the gist in OP, it is a fairly simple implementation. |
Although I agree with some kind of automatic injection and validation, marking an application class with I like the idea, but I think devs should have more control on what is happening. |
Not sure I agree with the idea of implementing an Interface as "coupling", I am open to different implementations though (including annotations). I do agree that requests with mix of sources can cause confusion, that is why this is an RFC and not an actual PR 😄 |
This could be done with ParamConverter, I don't see any benefit. |
@zajca ParamConverter is not in symfony core, it is part of framework-extra-bundle, ArgumentResolvers are however part of Symfony core. And yes it can be done in both with very small amount of code, so wouldn't it make sense to have this argument value resolver (DTO) in default arg value resolvers ? |
i agree with @zajca |
Thank you for this suggestion. |
Hello? This issue is about to be closed if nobody replies. |
Yep, I'd like to have this but I think it's probably covered by some other issue |
Thank you for this suggestion. |
Thank you for this suggestion. |
Friendly reminder that this issue exists. If I don't hear anything I'll close this. |
Hey, I didn't hear anything so I'm going to close it. Feel free to comment if this is still relevant, I can always reopen! |
…and `#[MapQueryString]` to map Request input to typed objects (Koc) This PR was merged into the 6.3 branch. Discussion ---------- [HttpKernel] Create Attributes `#[MapRequestPayload]` and `#[MapQueryString]` to map Request input to typed objects | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | #36037, #36093, #45628, #47425, #49002, #49134 | License | MIT | Doc PR | TBD Yet another variation of how we can map raw Request data to typed objects with validation. We can even build OpenApi Specification based on this DTO classes using [NelmioApiDocBundle](https://github.com/nelmio/NelmioApiDocBundle). ## Usage Example 🔧 ### `#[MapRequestPayload]` ```php class PostProductReviewPayload { 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, ) { } } class PostJsonApiController { public function __invoke( #[MapRequestPayload] PostProductReviewPayload $payload, ): Response { // $payload is validated and fully typed representation of the request body $request->getContent() // or $request->request->all() } } ``` ### `#[MapQueryString]` ```php class GetOrdersQuery { public function __construct( #[Assert\Valid] public readonly ?GetOrdersFilter $filter, #[Assert\LessThanOrEqual(500)] public readonly int $limit = 25, #[Assert\LessThanOrEqual(10_000)] public readonly int $offset = 0, ) { } } class GetOrdersFilter { public function __construct( #[Assert\Choice(['placed', 'shipped', 'delivered'])] public readonly ?string $status, public readonly ?float $total, ) { } } class GetApiController { public function __invoke( #[MapQueryString] GetOrdersQuery $query, ): Response { // $query is validated and fully typed representation of the query string $request->query->all() } } ``` ### Exception handling 💥 - Validation errors will result in an HTTP 422 response, accompanied by a serialized `ConstraintViolationList`. - Malformed data will be responded to with an HTTP 400 error. - Unsupported deserialization formats will be responded to with an HTTP 415 error. ## Comparison to another implementations 📑 Differences to #49002: - separate Attributes for explicit definition of the used source - no need to define which class use to map because Attributes already associated with typed argument - used ArgumentValueResolver mechanism instead of Subscribers - functional tests Differences to #49134: - it is possible to map whole query string to object, not per parameter - there is validation of the mapped object - supports both `$request->getContent()` and `$request->request->all()` mapping - functional tests Differences to #45628: - separate Attributes for explicit definition of the used source - supports `$request->request->all()` and `$request->query->all()` mapping - Exception handling opt-in - functional tests ## Bonus part 🎁 - Extracted `UnsupportedFormatException` which thrown when there is no decoder for a given format Commits ------- d987093 [HttpKernel] Create Attributes `#[MapRequestPayload]` and `#[MapQueryString]` to map Request input to typed objects
TLDR;
See gist linked in example .. implementation can change, it is just POC
Description
The idea is to have an
ArgumentValueResolver
which would convert request body (form data, json string and/or query string) to a DTO.(Initially shared this idea in symfony slack channel earlier today but thought it might get lost in a few hours so figured it is best to open an issue here on github)
So for example a request with body
"{ id: 12, name:' foo' }"
is made to a route, we can simply type hint our controller action with a DTO sayPerson
(containing$id
and$name
property). This argument resolver will automatically inject thePerson
DTO with$id
and$name
properties populated from request data.In order to check whether DTO should be injected by argument resolver or not we can have an interface say
RequestDTOInterface
(see the example linked below)Among many benefits, it would save us from writing a lot of boilerplate code especially with large payloads.
The nice thing about this RFC is, it doesn't add any new annotations and just re-uses what we already have in symfony framework.
Need a neat way to add it to "default argument value resolvers" , cannot add this in
HttpKernel
package as it depends uponSerializer
andValidator
componentsExample
I made a gist with a sample implementation (and tests), it uses Symfony serializer (
DenormalizerInterface
) to convert request content to DTO and also validates it using Symfony validatorhttps://gist.github.com/faizanakram99/33c97a5f5b834dceeb90003574c7b98d
The text was updated successfully, but these errors were encountered: