1313
1414namespace ApiPlatform \Validator \Metadata \Resource \Factory ;
1515
16+ use ApiPlatform \Metadata \ApiResource ;
1617use ApiPlatform \Metadata \HttpOperation ;
1718use ApiPlatform \Metadata \Parameter ;
1819use ApiPlatform \Metadata \Parameters ;
@@ -30,14 +31,19 @@ final class ParameterValidationResourceMetadataCollectionFactory implements Reso
3031 public function __construct (
3132 private readonly ?ResourceMetadataCollectionFactoryInterface $ decorated = null ,
3233 private readonly ?ContainerInterface $ filterLocator = null ,
34+ private readonly array $ defaultParameters = [],
3335 ) {
3436 }
3537
3638 public function create (string $ resourceClass ): ResourceMetadataCollection
3739 {
3840 $ resourceMetadataCollection = $ this ->decorated ?->create($ resourceClass ) ?? new ResourceMetadataCollection ($ resourceClass );
3941
42+ $ defaultParams = $ this ->buildDefaultParameters ();
43+
4044 foreach ($ resourceMetadataCollection as $ i => $ resource ) {
45+ $ resource = $ this ->applyDefaults ($ resource , $ defaultParams );
46+
4147 $ operations = $ resource ->getOperations ();
4248
4349 foreach ($ operations as $ operationName => $ operation ) {
@@ -135,4 +141,121 @@ private function addFilterValidation(HttpOperation $operation): Parameters
135141
136142 return $ parameters ;
137143 }
144+
145+ /**
146+ * Builds Parameter objects from the default configuration array.
147+ *
148+ * @return array<string, Parameter> Array of Parameter objects indexed by their key
149+ */
150+ private function buildDefaultParameters (): array
151+ {
152+ $ parameters = [];
153+
154+ foreach ($ this ->defaultParameters as $ parameterClass => $ config ) {
155+ if (!is_subclass_of ($ parameterClass , Parameter::class)) {
156+ continue ;
157+ }
158+
159+ $ key = $ config ['key ' ] ?? null ;
160+ if (!$ key ) {
161+ $ key = (new \ReflectionClass ($ parameterClass ))->getShortName ();
162+ }
163+
164+ $ identifier = $ key ;
165+
166+ $ parameter = $ this ->createParameterFromConfig ($ parameterClass , $ config );
167+ $ parameters [$ identifier ] = $ parameter ;
168+ }
169+
170+ return $ parameters ;
171+ }
172+
173+ /**
174+ * Creates a Parameter instance from configuration.
175+ *
176+ * @param class-string<Parameter> $parameterClass The parameter class name
177+ * @param array<string, mixed> $config The configuration array
178+ *
179+ * @return Parameter The created parameter instance
180+ */
181+ private function createParameterFromConfig (string $ parameterClass , array $ config ): Parameter
182+ {
183+ return new $ parameterClass (
184+ key: $ config ['key ' ] ?? null ,
185+ schema: $ config ['schema ' ] ?? null ,
186+ openApi: null ,
187+ provider: null ,
188+ filter: $ config ['filter ' ] ?? null ,
189+ property: $ config ['property ' ] ?? null ,
190+ description: $ config ['description ' ] ?? null ,
191+ properties: null ,
192+ required: $ config ['required ' ] ?? false ,
193+ priority: $ config ['priority ' ] ?? null ,
194+ hydra: $ config ['hydra ' ] ?? null ,
195+ constraints: $ config ['constraints ' ] ?? null ,
196+ security: $ config ['security ' ] ?? null ,
197+ securityMessage: $ config ['security_message ' ] ?? null ,
198+ extraProperties: $ config ['extra_properties ' ] ?? [],
199+ filterContext: null ,
200+ nativeType: null ,
201+ castToArray: null ,
202+ castToNativeType: null ,
203+ castFn: null ,
204+ default: $ config ['default ' ] ?? null ,
205+ filterClass: $ config ['filter_class ' ] ?? null ,
206+ );
207+ }
208+
209+ /**
210+ * Applies default parameters to the resource.
211+ *
212+ * @param array<string, Parameter> $defaultParams The default parameters to apply
213+ */
214+ private function applyDefaults (ApiResource $ resource , array $ defaultParams ): ApiResource
215+ {
216+ $ resourceParameters = $ resource ->getParameters () ?? new Parameters ();
217+ $ mergedResourceParameters = $ this ->mergeParameters ($ resourceParameters , $ defaultParams );
218+ $ resource = $ resource ->withParameters ($ mergedResourceParameters );
219+
220+ foreach ($ operations = $ resource ->getOperations () ?? [] as $ operationName => $ operation ) {
221+ $ operationParameters = $ operation ->getParameters () ?? new Parameters ();
222+ $ mergedOperationParameters = $ this ->mergeParameters ($ operationParameters , $ defaultParams );
223+ $ operations ->add ((string ) $ operationName , $ operation ->withParameters ($ mergedOperationParameters ));
224+ }
225+
226+ if ($ operations ) {
227+ $ resource = $ resource ->withOperations ($ operations );
228+ }
229+
230+ foreach ($ graphQlOperations = $ resource ->getGraphQlOperations () ?? [] as $ operationName => $ operation ) {
231+ $ operationParameters = $ operation ->getParameters () ?? new Parameters ();
232+ $ mergedOperationParameters = $ this ->mergeParameters ($ operationParameters , $ defaultParams );
233+ $ graphQlOperations [$ operationName ] = $ operation ->withParameters ($ mergedOperationParameters );
234+ }
235+
236+ if ($ graphQlOperations ) {
237+ $ resource = $ resource ->withGraphQlOperations ($ graphQlOperations );
238+ }
239+
240+ return $ resource ;
241+ }
242+
243+ /**
244+ * Merges default parameters with operation-specific parameters.
245+ *
246+ * @param Parameters $operationParameters The parameters already defined on the operation
247+ * @param array<string, Parameter> $defaultParams The default parameters to merge
248+ *
249+ * @return Parameters The merged parameters
250+ */
251+ private function mergeParameters (Parameters $ operationParameters , array $ defaultParams ): Parameters
252+ {
253+ $ merged = new Parameters ($ defaultParams );
254+
255+ foreach ($ operationParameters as $ key => $ param ) {
256+ $ merged ->add ($ key , $ param );
257+ }
258+
259+ return $ merged ;
260+ }
138261}
0 commit comments