diff --git a/src/Api/FilterInterface.php b/src/Api/FilterInterface.php index cb6936b14e8..9815b8b5fd0 100644 --- a/src/Api/FilterInterface.php +++ b/src/Api/FilterInterface.php @@ -28,6 +28,7 @@ interface FilterInterface * - type: the type of the filter * - required: if this filter is required * - strategy: the used strategy + * - is_collection (optional): is this filter is collection * - swagger (optional): additional parameters for the path operation, * e.g. 'swagger' => [ * 'description' => 'My Description', diff --git a/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php b/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php index 772b749743f..264bd6943eb 100644 --- a/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php +++ b/src/Bridge/Doctrine/Orm/Filter/SearchFilter.php @@ -112,6 +112,7 @@ public function getDescription(string $resourceClass): array 'type' => $typeOfField, 'required' => false, 'strategy' => $strategy, + 'is_collection' => '[]' === substr($filterParameterName, -2), ]; } } elseif ($metadata->hasAssociation($field)) { @@ -126,6 +127,7 @@ public function getDescription(string $resourceClass): array 'type' => 'string', 'required' => false, 'strategy' => self::STRATEGY_EXACT, + 'is_collection' => '[]' === substr($filterParameterName, -2), ]; } } diff --git a/src/Swagger/Serializer/DocumentationNormalizer.php b/src/Swagger/Serializer/DocumentationNormalizer.php index 1c0e1001a04..2c1ab3b169b 100644 --- a/src/Swagger/Serializer/DocumentationNormalizer.php +++ b/src/Swagger/Serializer/DocumentationNormalizer.php @@ -608,7 +608,11 @@ private function getFiltersParameters(string $resourceClass, string $operationNa 'in' => 'query', 'required' => $data['required'], ]; - $parameter += $this->getType($data['type'], false, null, null, $definitions, $serializerContext); + $parameter += $this->getType($data['type'], $data['is_collection'] ?? false, null, null, $definitions, $serializerContext); + + if ('array' === $parameter['type']) { + $parameter['collectionFormat'] = \in_array($data['type'], [Type::BUILTIN_TYPE_ARRAY, Type::BUILTIN_TYPE_OBJECT], true) ? 'csv' : 'multi'; + } if (isset($data['swagger'])) { $parameter = $data['swagger'] + $parameter; diff --git a/tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php b/tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php index 85c14d15bf6..bae5a9f0215 100644 --- a/tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php +++ b/tests/Bridge/Doctrine/Orm/Filter/SearchFilterTest.php @@ -65,144 +65,168 @@ public function testGetDescription() 'type' => 'int', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'id[]' => [ 'property' => 'id', 'type' => 'int', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'name' => [ 'property' => 'name', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'name[]' => [ 'property' => 'name', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'alias' => [ 'property' => 'alias', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'alias[]' => [ 'property' => 'alias', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'description' => [ 'property' => 'description', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'description[]' => [ 'property' => 'description', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummy' => [ 'property' => 'dummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummy[]' => [ 'property' => 'dummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummyDate' => [ 'property' => 'dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummyDate[]' => [ 'property' => 'dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummyFloat' => [ 'property' => 'dummyFloat', 'type' => 'float', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummyFloat[]' => [ 'property' => 'dummyFloat', 'type' => 'float', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummyPrice' => [ 'property' => 'dummyPrice', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummyPrice[]' => [ 'property' => 'dummyPrice', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'jsonData' => [ 'property' => 'jsonData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'jsonData[]' => [ 'property' => 'jsonData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'arrayData' => [ 'property' => 'arrayData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'arrayData[]' => [ 'property' => 'arrayData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'nameConverted' => [ 'property' => 'nameConverted', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'nameConverted[]' => [ 'property' => 'nameConverted', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummyBoolean' => [ 'property' => 'dummyBoolean', 'type' => 'bool', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummyBoolean[]' => [ 'property' => 'dummyBoolean', 'type' => 'bool', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], ], $filter->getDescription($this->resourceClass)); @@ -226,120 +250,140 @@ public function testGetDescription() 'type' => 'int', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'id[]' => [ 'property' => 'id', 'type' => 'int', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'name' => [ 'property' => 'name', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'name[]' => [ 'property' => 'name', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'alias' => [ 'property' => 'alias', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'alias[]' => [ 'property' => 'alias', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummy' => [ 'property' => 'dummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummy[]' => [ 'property' => 'dummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'dummyDate' => [ 'property' => 'dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'dummyDate[]' => [ 'property' => 'dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'jsonData' => [ 'property' => 'jsonData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'jsonData[]' => [ 'property' => 'jsonData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'arrayData' => [ 'property' => 'arrayData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'arrayData[]' => [ 'property' => 'arrayData', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'nameConverted' => [ 'property' => 'nameConverted', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'nameConverted[]' => [ 'property' => 'nameConverted', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'relatedDummies.dummyDate' => [ 'property' => 'relatedDummies.dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'relatedDummies.dummyDate[]' => [ 'property' => 'relatedDummies.dummyDate', 'type' => 'DateTimeInterface', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], 'relatedDummy' => [ 'property' => 'relatedDummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => false, ], 'relatedDummy[]' => [ 'property' => 'relatedDummy', 'type' => 'string', 'required' => false, 'strategy' => 'exact', + 'is_collection' => true, ], ], $filter->getDescription($this->resourceClass)); }