Skip to content

Commit

Permalink
Support for JMS discriminator map (#2247)
Browse files Browse the repository at this point in the history
  • Loading branch information
BesedinSasha committed Mar 30, 2024
1 parent 7f46161 commit 4ff744f
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 1 deletion.
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,16 @@
CHANGELOG
=========

4.25.0
-----
* Added support for [JMS @Discriminator](https://jmsyst.com/libs/serializer/master/reference/annotations#discriminator) annotation/attribute
```php
#[\JMS\Serializer\Annotation\Discriminator(field: 'type', map: ['car' => Car::class, 'plane' => Plane::class])]
abstract class Vehicle { }
class Car extends Vehicle { }
class Plane extends Vehicle { }
```

4.24.0
-----
* Added support for some integer ranges (https://phpstan.org/writing-php-code/phpdoc-types#integer-ranges).
Expand Down
19 changes: 18 additions & 1 deletion src/ModelDescriber/JMSModelDescriber.php
Expand Up @@ -15,6 +15,7 @@
use JMS\Serializer\Context;
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface;
Expand All @@ -33,6 +34,7 @@
class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
{
use ModelRegistryAwareTrait;
use ApplyOpenApiDiscriminatorTrait;

private $factory;

Expand Down Expand Up @@ -84,10 +86,25 @@ public function describe(Model $model, OA\Schema $schema)
{
$className = $model->getType()->getClassName();
$metadata = $this->factory->getMetadataForClass($className);
if (null === $metadata) {
if (!$metadata instanceof ClassMetadata) {
throw new \InvalidArgumentException(sprintf('No metadata found for class %s.', $className));
}

if (!empty($metadata->discriminatorFieldName)
&& $className === $metadata->discriminatorBaseClass
&& [] !== $metadata->discriminatorMap
&& Generator::UNDEFINED === $schema->discriminator) {
$this->applyOpenApiDiscriminator(
$model,
$schema,
$this->modelRegistry,
$metadata->discriminatorFieldName,
$metadata->discriminatorMap
);

return;
}

$annotationsReader = new AnnotationsReader(
$this->doctrineReader,
$this->modelRegistry,
Expand Down
15 changes: 15 additions & 0 deletions tests/Functional/Controller/JMSController80.php
Expand Up @@ -12,6 +12,7 @@
namespace Nelmio\ApiDocBundle\Tests\Functional\Controller;

use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\DiscriminatorMap\JMSAbstractUser;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSDualComplex;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSNamingStrategyConstraints;
Expand Down Expand Up @@ -151,4 +152,18 @@ public function minUserAction()
public function minUserNestedAction()
{
}

/**
* @Route("/api/jms_discriminator_map", methods={"GET"})
*
* @OA\Response(
* response=200,
* description="Success",
*
* @Model(type=JMSAbstractUser::class)
* )
*/
public function discriminatorMapAction()
{
}
}
11 changes: 11 additions & 0 deletions tests/Functional/Controller/JMSController81.php
Expand Up @@ -13,6 +13,7 @@

use Nelmio\ApiDocBundle\Annotation\Model;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\Article81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\DiscriminatorMap\JMSAbstractUser;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSDualComplex;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSNamingStrategyConstraints;
Expand Down Expand Up @@ -130,4 +131,14 @@ public function minUserNestedAction()
public function enum()
{
}

#[Route('/api/jms_discriminator_map', methods: ['GET'])]
#[OA\Response(
response: 200,
description: 'Success',
content: new Model(type: JMSAbstractUser::class))
]
public function discriminatorMapAction()
{
}
}
41 changes: 41 additions & 0 deletions tests/Functional/Entity/DiscriminatorMap/JMSAbstractUser.php
@@ -0,0 +1,41 @@
<?php

/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity\DiscriminatorMap;

use JMS\Serializer\Annotation as Serializer;
use Nelmio\ApiDocBundle\Tests\Functional\TestKernel;

if (TestKernel::isAnnotationsAvailable()) {
/**
* @Serializer\Discriminator(map={
* "manager" = JMSManager::class,
* "administrator" = JMSAdministrator::class,
* }, groups={"Default"})
*/
abstract class JMSAbstractUser
{
/**
* @Serializer\Type("string")
*
* @Serializer\Groups({"Default"})
*/
public $username;
}
} else {
#[Serializer\Discriminator(map: ['manager' => JMSManager::class, 'administrator' => JMSAdministrator::class], groups: ['Default'])]
abstract class JMSAbstractUser
{
#[Serializer\Type('string')]
#[Serializer\Groups(['Default'])]
public $username;
}
}
34 changes: 34 additions & 0 deletions tests/Functional/Entity/DiscriminatorMap/JMSAdministrator.php
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity\DiscriminatorMap;

use JMS\Serializer\Annotation as Serializer;
use Nelmio\ApiDocBundle\Tests\Functional\TestKernel;

if (TestKernel::isAnnotationsAvailable()) {
class JMSAdministrator extends JMSAbstractUser
{
/**
* @Serializer\Type("string")
*
* @Serializer\Groups({"Default"})
*/
public $adminTitle;
}
} else {
class JMSAdministrator extends JMSAbstractUser
{
#[Serializer\Type('string')]
#[Serializer\Groups(['Default'])]
public $adminTitle;
}
}
16 changes: 16 additions & 0 deletions tests/Functional/Entity/DiscriminatorMap/JMSManager.php
@@ -0,0 +1,16 @@
<?php

/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity\DiscriminatorMap;

class JMSManager extends JMSAbstractUser
{
}
41 changes: 41 additions & 0 deletions tests/Functional/JMSFunctionalTest.php
Expand Up @@ -372,6 +372,47 @@ public function testEnumSupport()
], json_decode($this->getModel('ArticleType81')->toJson(), true));
}

public function testModeDiscriminatorMap()
{
$this->assertEquals([
'type' => 'object',
'properties' => [
'username' => [
'type' => 'string',
],
],
'schema' => 'JMSManager',
], json_decode($this->getModel('JMSManager')->toJson(), true));

$this->assertEquals([
'type' => 'object',
'properties' => [
'username' => [
'type' => 'string',
],
'admin_title' => [
'type' => 'string',
],
],
'schema' => 'JMSAdministrator',
], json_decode($this->getModel('JMSAdministrator')->toJson(), true));

$this->assertEquals([
'oneOf' => [
['$ref' => '#/components/schemas/JMSManager'],
['$ref' => '#/components/schemas/JMSAdministrator'],
],
'schema' => 'JMSAbstractUser',
'discriminator' => [
'propertyName' => 'type',
'mapping' => [
'manager' => '#/components/schemas/JMSManager',
'administrator' => '#/components/schemas/JMSAdministrator',
],
],
], json_decode($this->getModel('JMSAbstractUser')->toJson(), true));
}

protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel(TestKernel::USE_JMS);
Expand Down

0 comments on commit 4ff744f

Please sign in to comment.