diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f25072..bf64357 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ CHANGELOG ----- * Added support of OpenAPI 3.0. The internals were completely reworked and this version introduces BC breaks. +3.7.0 +----- + +* Added `@SerializedName` annotation support and name converters when using Symfony >= 4.2. + 3.3.0 ----- diff --git a/ModelDescriber/ObjectModelDescriber.php b/ModelDescriber/ObjectModelDescriber.php index 3c33385..4a26100 100644 --- a/ModelDescriber/ObjectModelDescriber.php +++ b/ModelDescriber/ObjectModelDescriber.php @@ -21,6 +21,7 @@ use Nelmio\ApiDocBundle\PropertyDescriber\PropertyDescriberInterface; use OpenApi\Annotations as OA; use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface { @@ -34,6 +35,8 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar private $propertyDescribers; /** @var string[] */ private $mediaTypes; + /** @var NameConverterInterface[] */ + private $nameConverter; private $swaggerDefinitionAnnotationReader; @@ -41,12 +44,14 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar PropertyInfoExtractorInterface $propertyInfo, Reader $reader, $propertyDescribers, - array $mediaTypes + array $mediaTypes, + NameConverterInterface $nameConverter = null ) { $this->propertyInfo = $propertyInfo; $this->doctrineReader = $reader; $this->propertyDescribers = $propertyDescribers; $this->mediaTypes = $mediaTypes; + $this->nameConverter = $nameConverter; } public function describe(Model $model, OA\Schema $schema) @@ -70,10 +75,12 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar } foreach ($propertyInfoProperties as $propertyName) { + $serializedName = null !== $this->nameConverter ? $this->nameConverter->normalize($propertyName, $class, null, null !== $model->getGroups() ? ['groups' => $model->getGroups()] : []) : $propertyName; + // read property options from OpenApi Property annotation if it exists if (property_exists($class, $propertyName)) { $reflectionProperty = new \ReflectionProperty($class, $propertyName); - $property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $propertyName)); + $property = Util::getProperty($schema, $annotationsReader->getPropertyName($reflectionProperty, $serializedName)); $groups = $model->getGroups(); if (isset($groups[$propertyName]) && is_array($groups[$propertyName])) { @@ -82,7 +89,7 @@ class ObjectModelDescriber implements ModelDescriberInterface, ModelRegistryAwar $annotationsReader->updateProperty($reflectionProperty, $property, $groups); } else { - $property = Util::getProperty($schema, $propertyName); + $property = Util::getProperty($schema, $serializedName); } // If type manually defined diff --git a/Resources/config/services.xml b/Resources/config/services.xml index a511711..91cda00 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -43,6 +43,7 @@ + diff --git a/Tests/Functional/Controller/SerializedNameController.php b/Tests/Functional/Controller/SerializedNameController.php new file mode 100644 index 0000000..736a266 --- /dev/null +++ b/Tests/Functional/Controller/SerializedNameController.php @@ -0,0 +1,37 @@ += 4.2). + * + * @Route("/api", host="api.example.com") + */ +class SerializedNameController +{ + /** + * @OA\Response( + * response="200", + * description="success", + * @Model(type=SerializedNameEnt::class) + * ) + * @Route("/serializename", methods={"GET"}) + */ + public function serializedNameAction() + { + } +} diff --git a/Tests/Functional/EntityExcluded/SerializedNameEnt.php b/Tests/Functional/EntityExcluded/SerializedNameEnt.php new file mode 100644 index 0000000..a7dc459 --- /dev/null +++ b/Tests/Functional/EntityExcluded/SerializedNameEnt.php @@ -0,0 +1,36 @@ + + */ +class SerializedNameEnt +{ + /** + * @SerializedName("notfoo") + * + * @var string + */ + public $foo; + + /** + * Tests serialized name feature. + * + * @SerializedName("notwhatyouthink") + */ + public function setBar(string $bar) + { + } +} diff --git a/Tests/Functional/FunctionalTest.php b/Tests/Functional/FunctionalTest.php index 263fd50..6e9ea49 100644 --- a/Tests/Functional/FunctionalTest.php +++ b/Tests/Functional/FunctionalTest.php @@ -13,6 +13,7 @@ namespace Nelmio\ApiDocBundle\Tests\Functional; use Nelmio\ApiDocBundle\OpenApiPhp\Util; use OpenApi\Annotations as OA; +use Symfony\Component\Serializer\Annotation\SerializedName; class FunctionalTest extends WebTestCase { @@ -409,4 +410,20 @@ class FunctionalTest extends WebTestCase { $this->assertNotHasParameter('name', 'path', $this->getOperation('/api/article/{id}', 'get')); } + + public function testSerializedNameAction() + { + if (!class_exists(SerializedName::class)) { + $this->markTestSkipped('Annotation @SerializedName doesn\'t exist.'); + } + + $model = $this->getModel('SerializedNameEnt'); + $this->assertCount(2, $model->properties); + + $this->assertNotHasProperty('foo', $model); + $this->assertHasProperty('notfoo', $model); + + $this->assertNotHasProperty('bar', $model); + $this->assertHasProperty('notwhatyouthink', $model); + } } diff --git a/Tests/Functional/TestKernel.php b/Tests/Functional/TestKernel.php index 7643f0c..993d46b 100644 --- a/Tests/Functional/TestKernel.php +++ b/Tests/Functional/TestKernel.php @@ -29,6 +29,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Routing\RouteCollectionBuilder; +use Symfony\Component\Serializer\Annotation\SerializedName; class TestKernel extends Kernel { @@ -87,6 +88,10 @@ class TestKernel extends Kernel $routes->add('/docs/{area}', 'nelmio_api_doc.controller.swagger_ui')->setDefault('area', 'default'); $routes->add('/docs.json', 'nelmio_api_doc.controller.swagger'); + if (class_exists(SerializedName::class)) { + $routes->import(__DIR__.'/Controller/SerializedNameController.php', '/', 'annotation'); + } + if (class_exists(FOSRestBundle::class)) { $routes->import(__DIR__.'/Controller/FOSRestController.php', '/', 'annotation'); }