11
11
12
12
namespace Symfony \Component \HttpKernel \Controller \ArgumentResolver ;
13
13
14
+ use Symfony \Component \EventDispatcher \EventSubscriberInterface ;
14
15
use Symfony \Component \HttpFoundation \Request ;
15
16
use Symfony \Component \HttpFoundation \Response ;
16
17
use Symfony \Component \HttpKernel \Attribute \MapQueryString ;
17
18
use Symfony \Component \HttpKernel \Attribute \MapRequestPayload ;
18
19
use Symfony \Component \HttpKernel \Controller \ValueResolverInterface ;
19
20
use Symfony \Component \HttpKernel \ControllerMetadata \ArgumentMetadata ;
21
+ use Symfony \Component \HttpKernel \Event \ControllerArgumentsEvent ;
20
22
use Symfony \Component \HttpKernel \Exception \HttpException ;
23
+ use Symfony \Component \HttpKernel \KernelEvents ;
21
24
use Symfony \Component \Serializer \Exception \NotEncodableValueException ;
22
25
use Symfony \Component \Serializer \Exception \PartialDenormalizationException ;
23
26
use Symfony \Component \Serializer \Exception \UnsupportedFormatException ;
31
34
32
35
/**
33
36
* @author Konstantin Myakshin <molodchick@gmail.com>
37
+ *
38
+ * @final
34
39
*/
35
- final class RequestPayloadValueResolver implements ValueResolverInterface
40
+ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscriberInterface
36
41
{
37
42
/**
38
43
* @see \Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT
@@ -59,24 +64,43 @@ public function __construct(
59
64
60
65
public function resolve (Request $ request , ArgumentMetadata $ argument ): iterable
61
66
{
62
- $ payloadMappers = [
63
- MapQueryString::class => ['mapQueryString ' , Response::HTTP_NOT_FOUND ],
64
- MapRequestPayload::class => ['mapRequestPayload ' , Response::HTTP_UNPROCESSABLE_ENTITY ],
65
- ];
67
+ $ attribute = $ argument ->getAttributesOfType (MapQueryString::class, ArgumentMetadata::IS_INSTANCEOF )[0 ]
68
+ ?? $ argument ->getAttributesOfType (MapRequestPayload::class, ArgumentMetadata::IS_INSTANCEOF )[0 ]
69
+ ?? null ;
70
+
71
+ if (!$ attribute ) {
72
+ return [];
73
+ }
74
+
75
+ $ attribute ->metadata = $ argument ;
76
+
77
+ return [$ attribute ];
78
+ }
66
79
67
- foreach ($ payloadMappers as $ mappingAttribute => [$ payloadMapper , $ validationFailedCode ]) {
68
- if (!$ attributes = $ argument ->getAttributesOfType ($ mappingAttribute , ArgumentMetadata::IS_INSTANCEOF )) {
80
+ public function onKernelControllerArguments (ControllerArgumentsEvent $ event ): void
81
+ {
82
+ $ arguments = $ event ->getArguments ();
83
+
84
+ foreach ($ arguments as $ i => $ argument ) {
85
+ if ($ argument instanceof MapQueryString) {
86
+ $ payloadMapper = 'mapQueryString ' ;
87
+ $ validationFailedCode = Response::HTTP_NOT_FOUND ;
88
+ } elseif ($ argument instanceof MapRequestPayload) {
89
+ $ payloadMapper = 'mapRequestPayload ' ;
90
+ $ validationFailedCode = Response::HTTP_UNPROCESSABLE_ENTITY ;
91
+ } else {
69
92
continue ;
70
93
}
94
+ $ request = $ event ->getRequest ();
71
95
72
- if (!$ type = $ argument ->getType ()) {
73
- throw new \LogicException (sprintf ('Could not resolve the "$%s" controller argument: argument should be typed. ' , $ argument ->getName ()));
96
+ if (!$ type = $ argument ->metadata -> getType ()) {
97
+ throw new \LogicException (sprintf ('Could not resolve the "$%s" controller argument: argument should be typed. ' , $ argument ->metadata -> getName ()));
74
98
}
75
99
76
100
if ($ this ->validator ) {
77
101
$ violations = new ConstraintViolationList ();
78
102
try {
79
- $ payload = $ this ->$ payloadMapper ($ request , $ type , $ attributes [ 0 ] );
103
+ $ payload = $ this ->$ payloadMapper ($ request , $ type , $ argument );
80
104
} catch (PartialDenormalizationException $ e ) {
81
105
$ trans = $ this ->translator ? $ this ->translator ->trans (...) : fn ($ m , $ p ) => strtr ($ m , $ p );
82
106
foreach ($ e ->getErrors () as $ error ) {
@@ -92,26 +116,35 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable
92
116
}
93
117
94
118
if (null !== $ payload ) {
95
- $ violations ->addAll ($ this ->validator ->validate ($ payload , null , $ attributes [ 0 ] ->validationGroups ?? null ));
119
+ $ violations ->addAll ($ this ->validator ->validate ($ payload , null , $ argument ->validationGroups ?? null ));
96
120
}
97
121
98
122
if (\count ($ violations )) {
99
123
throw new HttpException ($ validationFailedCode , implode ("\n" , array_map (static fn ($ e ) => $ e ->getMessage (), iterator_to_array ($ violations ))), new ValidationFailedException ($ payload , $ violations ));
100
124
}
101
125
} else {
102
126
try {
103
- $ payload = $ this ->$ payloadMapper ($ request , $ type , $ attributes [ 0 ] );
127
+ $ payload = $ this ->$ payloadMapper ($ request , $ type , $ argument );
104
128
} catch (PartialDenormalizationException $ e ) {
105
129
throw new HttpException ($ validationFailedCode , implode ("\n" , array_map (static fn ($ e ) => $ e ->getMessage (), $ e ->getErrors ())), $ e );
106
130
}
107
131
}
108
132
109
- if (null !== $ payload || $ argument ->isNullable ()) {
110
- return [ $ payload ] ;
133
+ if (null === $ payload && ! $ argument-> metadata ->isNullable ()) {
134
+ throw new HttpException ( $ validationFailedCode ) ;
111
135
}
136
+
137
+ $ arguments [$ i ] = $ payload ;
112
138
}
113
139
114
- return [];
140
+ $ event ->setArguments ($ arguments );
141
+ }
142
+
143
+ public static function getSubscribedEvents (): array
144
+ {
145
+ return [
146
+ KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments ' ,
147
+ ];
115
148
}
116
149
117
150
private function mapQueryString (Request $ request , string $ type , MapQueryString $ attribute ): ?object
0 commit comments