Skip to content

Commit

Permalink
Add support for forced enum name serialization using JMS
Browse files Browse the repository at this point in the history
  • Loading branch information
bobvandevijver committed Oct 31, 2024
1 parent 1e283ef commit 0237c9d
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/Model/ModelRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'
Expand Down
7 changes: 5 additions & 2 deletions src/ModelDescriber/EnumModelDescriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
9 changes: 9 additions & 0 deletions src/ModelDescriber/JMSModelDescriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions tests/Functional/Entity/JMSEnum81.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ class JMSEnum81
#[Serializer\Type('array<enum<'.ArticleType81::class.", 'value'>>")]
#[Serializer\Expose]
public $enumValues;

#[Serializer\Type('enum<'.ArticleType81::class.", 'name'>")]
#[Serializer\Expose]
public $enumName;

#[Serializer\Type('array<enum<'.ArticleType81::class.", 'name'>>")]
#[Serializer\Expose]
public $enumNames;
}
18 changes: 18 additions & 0 deletions tests/Functional/JMSFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
}
Expand Down

0 comments on commit 0237c9d

Please sign in to comment.