diff --git a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php index 57ec89787..4994c1851 100644 --- a/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php +++ b/ModelDescriber/Annotations/SymfonyConstraintAnnotationReader.php @@ -12,6 +12,7 @@ namespace Nelmio\ApiDocBundle\ModelDescriber\Annotations; use Doctrine\Common\Annotations\Reader; +use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Annotations as OA; use Symfony\Component\Validator\Constraints as Assert; @@ -81,8 +82,7 @@ public function updateProperty($reflection, OA\Property $property): void $property->minItems = (int) $annotation->min; $property->maxItems = (int) $annotation->max; } elseif ($annotation instanceof Assert\Choice) { - $values = $annotation->callback ? call_user_func(is_array($annotation->callback) ? $annotation->callback : [$reflection->class, $annotation->callback]) : $annotation->choices; - $property->enum = array_values($values); + $this->applyEnumFromChoiceConstraint($property, $annotation, $reflection); } elseif ($annotation instanceof Assert\Range) { $property->minimum = (int) $annotation->min; $property->maximum = (int) $annotation->max; @@ -131,4 +131,23 @@ private function appendPattern(OA\Schema $property, $newPattern): void $property->pattern = $newPattern; } } + + /** + * @var ReflectionProperty|ReflectionClass + */ + private function applyEnumFromChoiceConstraint(OA\Schema $property, Assert\Choice $choice, $reflection): void + { + if ($choice->callback) { + $enumValues = call_user_func(is_array($choice->callback) ? $choice->callback : [$reflection->class, $choice->callback]); + } else { + $enumValues = $choice->choices; + } + + $setEnumOnThis = $property; + if ($choice->multiple) { + $setEnumOnThis = Util::getChild($property, OA\Items::class); + } + + $setEnumOnThis->enum = array_values($enumValues); + } } diff --git a/Tests/Functional/Entity/SymfonyConstraints.php b/Tests/Functional/Entity/SymfonyConstraints.php index 2fa149e97..0cada3d6a 100644 --- a/Tests/Functional/Entity/SymfonyConstraints.php +++ b/Tests/Functional/Entity/SymfonyConstraints.php @@ -71,6 +71,13 @@ class SymfonyConstraints */ private $propertyChoiceWithCallbackWithoutClass; + /** + * @var string[] + * + * @Assert\Choice(multiple=true, choices={"choice1", "choice2"}) + */ + private $propertyChoiceWithMultiple; + /** * @var int * @@ -145,6 +152,11 @@ public function setPropertyChoiceWithCallbackWithoutClass(int $propertyChoiceWit $this->propertyChoiceWithCallbackWithoutClass = $propertyChoiceWithCallbackWithoutClass; } + public function setPropertyChoiceWithMultiple(array $propertyChoiceWithMultiple): void + { + $this->propertyChoiceWithMultiple = $propertyChoiceWithMultiple; + } + public function setPropertyExpression(int $propertyExpression): void { $this->propertyExpression = $propertyExpression; diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index e9a86762b..64a194ba5 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -395,6 +395,13 @@ public function testSymfonyConstraintDocumentation() 'type' => 'integer', 'enum' => ['choice1', 'choice2'], ], + 'propertyChoiceWithMultiple' => [ + 'type' => 'array', + 'items' => [ + 'type' => 'string', + 'enum' => ['choice1', 'choice2'], + ], + ], 'propertyExpression' => [ 'type' => 'integer', ], diff --git a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php index 876f75b12..585f19d21 100644 --- a/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php +++ b/Tests/ModelDescriber/Annotations/SymfonyConstraintAnnotationReaderTest.php @@ -106,6 +106,27 @@ public function testAssertChoiceResultsInNumericArray() $this->assertEquals($schema->properties[0]->enum, ['active', 'blocked']); } + public function testMultipleChoiceConstraintsApplyEnumToItems() + { + $entity = new class() { + /** + * @Assert\Choice(choices={"one", "two"}, multiple=true) + */ + private $property1; + }; + + $schema = new OA\Schema([]); + $schema->merge([new OA\Property(['property' => 'property1'])]); + + $symfonyConstraintAnnotationReader = new SymfonyConstraintAnnotationReader(new AnnotationReader()); + $symfonyConstraintAnnotationReader->setSchema($schema); + + $symfonyConstraintAnnotationReader->updateProperty(new \ReflectionProperty($entity, 'property1'), $schema->properties[0]); + + $this->assertInstanceOf(OA\Items::class, $schema->properties[0]->items); + $this->assertEquals($schema->properties[0]->items->enum, ['one', 'two']); + } + /** * @group https://github.com/nelmio/NelmioApiDocBundle/issues/1780 */