1313
1414namespace ApiPlatform \Hydra \Serializer ;
1515
16+ use ApiPlatform \Hydra \IriTemplateMapping ;
17+ use ApiPlatform \Hydra \State \Util \SearchHelperTrait ;
1618use ApiPlatform \JsonLd \Serializer \HydraPrefixTrait ;
1719use ApiPlatform \Metadata \FilterInterface ;
18- use ApiPlatform \Metadata \Parameters ;
19- use ApiPlatform \Metadata \QueryParameterInterface ;
2020use ApiPlatform \Metadata \Resource \Factory \ResourceMetadataCollectionFactoryInterface ;
2121use ApiPlatform \Metadata \ResourceClassResolverInterface ;
2222use ApiPlatform \State \Util \StateOptionsTrait ;
3434final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface
3535{
3636 use HydraPrefixTrait;
37+ use SearchHelperTrait;
3738 use StateOptionsTrait;
3839 private ?ContainerInterface $ filterLocator = null ;
3940
@@ -108,7 +109,13 @@ public function normalize(mixed $object, ?string $format = null, array $context
108109
109110 if ($ currentFilters || ($ parameters && \count ($ parameters ))) {
110111 $ hydraPrefix = $ this ->getHydraPrefix ($ context + $ this ->defaultContext );
111- $ data [$ hydraPrefix .'search ' ] = $ this ->getSearch ($ resourceClass , $ requestParts , $ currentFilters , $ parameters , $ hydraPrefix );
112+ ['mapping ' => $ mapping , 'keys ' => $ keys ] = $ this ->getSearchMappingAndKeys ($ operation , $ resourceClass , $ currentFilters , $ parameters , [$ this , 'getFilter ' ]);
113+ $ data [$ hydraPrefix .'search ' ] = [
114+ '@type ' => $ hydraPrefix .'IriTemplate ' ,
115+ $ hydraPrefix .'template ' => \sprintf ('%s{?%s} ' , $ requestParts ['path ' ], implode (', ' , $ keys )),
116+ $ hydraPrefix .'variableRepresentation ' => 'BasicRepresentation ' ,
117+ $ hydraPrefix .'mapping ' => $ this ->convertMappingToArray ($ mapping ),
118+ ];
112119 }
113120
114121 return $ data ;
@@ -125,88 +132,28 @@ public function setNormalizer(NormalizerInterface $normalizer): void
125132 }
126133
127134 /**
128- * Returns the content of the Hydra search property.
135+ * @param list<IriTemplateMapping> $mapping
129136 *
130- * @param FilterInterface[] $filters
137+ * @return array<array<string, mixed>>
131138 */
132- private function getSearch ( string $ resourceClass , array $ parts , array $ filters , ? Parameters $ parameters , string $ hydraPrefix ): array
139+ private function convertMappingToArray ( array $ mapping ): array
133140 {
134- $ variables = [];
135- $ mapping = [];
136- foreach ($ filters as $ filter ) {
137- foreach ($ filter ->getDescription ($ resourceClass ) as $ variable => $ data ) {
138- $ variables [] = $ variable ;
139- $ mapping [] = ['@type ' => 'IriTemplateMapping ' , 'variable ' => $ variable , 'property ' => $ data ['property ' ] ?? null , 'required ' => $ data ['required ' ] ?? false ];
140- }
141- }
142-
143- foreach ($ parameters ?? [] as $ key => $ parameter ) {
144- // Each IriTemplateMapping maps a variable used in the template to a property
145- if (!$ parameter instanceof QueryParameterInterface || false === $ parameter ->getHydra ()) {
146- continue ;
147- }
148-
149- if (($ filterId = $ parameter ->getFilter ()) && \is_string ($ filterId ) && ($ filter = $ this ->getFilter ($ filterId ))) {
150- $ filterDescription = $ filter ->getDescription ($ resourceClass );
151-
152- foreach ($ filterDescription as $ variable => $ description ) {
153- // // This is a practice induced by PHP and is not necessary when implementing URI template
154- if (str_ends_with ((string ) $ variable , '[] ' )) {
155- continue ;
156- }
157-
158- if (!($ descriptionProperty = $ description ['property ' ] ?? null )) {
159- continue ;
160- }
161-
162- if (($ prop = $ parameter ->getProperty ()) && $ descriptionProperty !== $ prop ) {
163- continue ;
164- }
165-
166- // :property is a pattern allowed when defining parameters
167- $ k = str_replace (':property ' , $ descriptionProperty , $ key );
168- $ variable = str_replace ($ descriptionProperty , $ k , $ variable );
169- $ variables [] = $ variable ;
170- $ m = ['@type ' => 'IriTemplateMapping ' , 'variable ' => $ variable , 'property ' => $ descriptionProperty ];
171- if (null !== ($ required = $ parameter ->getRequired () ?? $ description ['required ' ] ?? null )) {
172- $ m ['required ' ] = $ required ;
173- }
174- $ mapping [] = $ m ;
175- }
176-
177- if ($ filterDescription ) {
178- continue ;
179- }
141+ $ convertedMapping = [];
142+ foreach ($ mapping as $ m ) {
143+ $ converted = [
144+ '@type ' => 'IriTemplateMapping ' ,
145+ 'variable ' => $ m ->variable ,
146+ 'property ' => $ m ->property ,
147+ ];
148+
149+ if (null !== ($ r = $ m ->required )) {
150+ $ converted ['required ' ] = $ r ;
180151 }
181152
182- if (str_contains ($ key , ':property ' ) && $ parameter ->getProperties ()) {
183- $ required = $ parameter ->getRequired ();
184- foreach ($ parameter ->getProperties () as $ prop ) {
185- $ k = str_replace (':property ' , $ prop , $ key );
186- $ m = ['@type ' => 'IriTemplateMapping ' , 'variable ' => $ k , 'property ' => $ prop ];
187- $ variables [] = $ k ;
188- if (null !== $ required ) {
189- $ m ['required ' ] = $ required ;
190- }
191- $ mapping [] = $ m ;
192- }
193-
194- continue ;
195- }
196-
197- if (!($ property = $ parameter ->getProperty ())) {
198- continue ;
199- }
200-
201- $ m = ['@type ' => 'IriTemplateMapping ' , 'variable ' => $ key , 'property ' => $ property ];
202- $ variables [] = $ key ;
203- if (null !== ($ required = $ parameter ->getRequired ())) {
204- $ m ['required ' ] = $ required ;
205- }
206- $ mapping [] = $ m ;
153+ $ convertedMapping [] = $ converted ;
207154 }
208155
209- return [ ' @type ' => $ hydraPrefix . ' IriTemplate ' , $ hydraPrefix . ' template ' => \sprintf ( ' %s{?%s} ' , $ parts [ ' path ' ], implode ( ' , ' , $ variables )), $ hydraPrefix . ' variableRepresentation ' => ' BasicRepresentation ' , $ hydraPrefix . ' mapping ' => $ mapping ] ;
156+ return $ convertedMapping ;
210157 }
211158
212159 /**
0 commit comments