diff --git a/src/Model/ModelRegistry.php b/src/Model/ModelRegistry.php index d258d3f14..a7e931324 100644 --- a/src/Model/ModelRegistry.php +++ b/src/Model/ModelRegistry.php @@ -12,6 +12,7 @@ namespace Nelmio\ApiDocBundle\Model; use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface; +use Nelmio\ApiDocBundle\ModelDescriber\EnumModelDescriber; use Nelmio\ApiDocBundle\ModelDescriber\ModelDescriberInterface; use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Annotations as OA; @@ -145,6 +146,10 @@ public function registerSchemas(): void private function generateModelName(Model $model): string { $name = $base = $this->getTypeShortName($model->getType()); + if (isset($model->getSerializationContext()[EnumModelDescriber::FORCE_NAMES]) && true === $model->getSerializationContext()[EnumModelDescriber::FORCE_NAMES]) { + $name .= 'Name'; + } + $names = array_column( $this->api->components instanceof OA\Components && is_array($this->api->components->schemas) ? $this->api->components->schemas : [], 'schema' diff --git a/src/ModelDescriber/EnumModelDescriber.php b/src/ModelDescriber/EnumModelDescriber.php index 1aae41ef1..c75935bfe 100644 --- a/src/ModelDescriber/EnumModelDescriber.php +++ b/src/ModelDescriber/EnumModelDescriber.php @@ -17,17 +17,20 @@ class EnumModelDescriber implements ModelDescriberInterface { + public const FORCE_NAMES = '_nelmio_enum_force_names'; + public function describe(Model $model, Schema $schema) { $enumClass = $model->getType()->getClassName(); + $forceName = isset($model->getSerializationContext()[self::FORCE_NAMES]) && true === $model->getSerializationContext()[self::FORCE_NAMES]; $enums = []; foreach ($enumClass::cases() as $enumCase) { - $enums[] = $enumCase->value; + $enums[] = $forceName ? $enumCase->name : $enumCase->value; } $reflectionEnum = new \ReflectionEnum($enumClass); - if ($reflectionEnum->isBacked() && 'int' === $reflectionEnum->getBackingType()->getName()) { + if (!$forceName && $reflectionEnum->isBacked() && 'int' === $reflectionEnum->getBackingType()->getName()) { $schema->type = 'integer'; } else { $schema->type = 'string'; diff --git a/src/ModelDescriber/JMSModelDescriber.php b/src/ModelDescriber/JMSModelDescriber.php index b6793889f..91006dd16 100644 --- a/src/ModelDescriber/JMSModelDescriber.php +++ b/src/ModelDescriber/JMSModelDescriber.php @@ -336,6 +336,15 @@ public function describeItem(array $type, OA\Schema $property, Context $context, if (is_string($typeParam) && enum_exists($typeParam)) { $type['name'] = $typeParam; } + + if (isset($type['params'][1])) { + if ('value' !== $type['params'][1] && is_a($type['name'], \BackedEnum::class, true)) { + // In case of a backed enum, it is possible to serialize it using its names instead of values + // Set a specific serialization context property to enforce a new model, as options cannot be used to force a new model + // See https://github.com/schmittjoh/serializer/blob/5a5a03a71a28a480189c5a0ca95893c19f1d120c/src/Handler/EnumHandler.php#L47 + $serializationContext[EnumModelDescriber::FORCE_NAMES] = true; + } + } } $groups = $this->computeGroups($context, $type); diff --git a/tests/Functional/Entity/JMSEnum81.php b/tests/Functional/Entity/JMSEnum81.php index ee5b48ee1..7c2c6f6a9 100644 --- a/tests/Functional/Entity/JMSEnum81.php +++ b/tests/Functional/Entity/JMSEnum81.php @@ -22,4 +22,12 @@ class JMSEnum81 #[Serializer\Type('array>")] #[Serializer\Expose] public $enumValues; + + #[Serializer\Type('enum<'.ArticleType81::class.", 'name'>")] + #[Serializer\Expose] + public $enumName; + + #[Serializer\Type('array>")] + #[Serializer\Expose] + public $enumNames; } diff --git a/tests/Functional/JMSFunctionalTest.php b/tests/Functional/JMSFunctionalTest.php index d491b9f00..d98c95f23 100644 --- a/tests/Functional/JMSFunctionalTest.php +++ b/tests/Functional/JMSFunctionalTest.php @@ -380,6 +380,15 @@ public function testEnumSupport(): void ] ], json_decode($this->getModel('ArticleType81')->toJson(), true)); + self::assertEquals([ + 'schema' => 'ArticleType81Name', + 'type' => 'string', + 'enum' => [ + 'DRAFT', + 'FINAL' + ] + ], json_decode($this->getModel('ArticleType81Name')->toJson(), true)); + if (TestKernel::isAnnotationsAvailable()) { // Further tests have only been defined with attributes return; @@ -398,6 +407,15 @@ public function testEnumSupport(): void '$ref' => '#/components/schemas/ArticleType81' ] ], + 'enum_name' => [ + '$ref' => '#/components/schemas/ArticleType81Name' + ], + 'enum_names' => [ + 'type' => 'array', + 'items' => [ + '$ref' => '#/components/schemas/ArticleType81Name' + ] + ], ] ], json_decode($this->getModel('JMSEnum81')->toJson(), true)); }