Skip to content

Commit

Permalink
feat: add support for name based serialisation of JMS enums (#2355)
Browse files Browse the repository at this point in the history
| Q | A |

|---------------|---------------------------------------------------------------------------------------------------------------------------|
| Bug fix? | no |
| New feature? | yes <!-- please update src/**/CHANGELOG.md files --> |
| Deprecations? | no <!-- please update UPGRADE-*.md and
src/**/CHANGELOG.md files --> |
| Issues | - <!-- prefix each issue number with "Fix #", no need to
create an issue if none exists, explain below instead --> |

<!--
Replace this notice by a description of your feature/bugfix.
This will help reviewers and should be a good start for the
documentation.

Additionally:
 - Always add tests and ensure they pass.
- For new features, provide some code snippets to help understand usage.
-->

This PR add a method to detect when the developer has annotated their
property with a JMS type that specifies that the backed enum needs to be
serialized with its name instead of value. I needed to use the
serialization context as the model options are not included in the model
hash, while I do need to generate a new unique description. I've chosen
to append `Name` to the resulting Model name.

Depends on #2372

(cherry picked from commit 9ee5f58)
  • Loading branch information
bobvandevijver authored and DjordyKoert committed Oct 31, 2024
1 parent 756785b commit 82c6066
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 2 deletions.
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 @@ -330,6 +330,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/JMSEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ class JMSEnum
#[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 @@ -385,6 +385,15 @@ public function testEnumSupport(): void
return;
}

self::assertEquals([
'schema' => 'ArticleType812',
'type' => 'string',
'enum' => [
'DRAFT',
'FINAL'
]
], json_decode($this->getModel('ArticleType812')->toJson(), true));

self::assertEquals([
'schema' => 'JMSEnum',
'type' => 'object',
Expand All @@ -398,6 +407,15 @@ public function testEnumSupport(): void
'$ref' => '#/components/schemas/ArticleType81'
]
],
'enum_name' => [
'$ref' => '#/components/schemas/ArticleType812'
],
'enum_names' => [
'type' => 'array',
'items' => [
'$ref' => '#/components/schemas/ArticleType812'
]
],
]
], json_decode($this->getModel('JMSEnum')->toJson(), true));
}
Expand Down

0 comments on commit 82c6066

Please sign in to comment.