diff --git a/composer.json b/composer.json
index 3303bccd7..3ff9ab761 100644
--- a/composer.json
+++ b/composer.json
@@ -56,6 +56,7 @@
"symfony/stopwatch": "^5.4 || ^6.4 || ^7.0",
"symfony/templating": "^5.4 || ^6.4 || ^7.0",
"symfony/twig-bundle": "^5.4 || ^6.4 || ^7.0",
+ "symfony/uid": "^5.4 || ^6.4 || ^7.0",
"symfony/validator": "^5.4 || ^6.4 || ^7.0",
"willdurand/hateoas-bundle": "^1.0 || ^2.0"
},
diff --git a/config/services.xml b/config/services.xml
index 75873f9c7..3382906f2 100644
--- a/config/services.xml
+++ b/config/services.xml
@@ -151,6 +151,10 @@
+
+
+
+
diff --git a/src/DependencyInjection/NelmioApiDocExtension.php b/src/DependencyInjection/NelmioApiDocExtension.php
index 81f927330..4376aa8a1 100644
--- a/src/DependencyInjection/NelmioApiDocExtension.php
+++ b/src/DependencyInjection/NelmioApiDocExtension.php
@@ -171,6 +171,10 @@ public function load(array $configs, ContainerBuilder $container): void
$container->registerForAutoconfiguration(ModelDescriberInterface::class)
->addTag('nelmio_api_doc.model_describer');
+ if (!class_exists(\Symfony\Component\Uid\AbstractUid::class)) {
+ $container->removeDefinition('nelmio_api_doc.object_model.property_describers.uuid');
+ }
+
// Import services needed for each library
$loader->load('php_doc.xml');
diff --git a/src/PropertyDescriber/UuidPropertyDescriber.php b/src/PropertyDescriber/UuidPropertyDescriber.php
new file mode 100644
index 000000000..865124f4f
--- /dev/null
+++ b/src/PropertyDescriber/UuidPropertyDescriber.php
@@ -0,0 +1,32 @@
+type = 'string';
+ $property->format = 'uuid';
+ }
+
+ public function supports(array $types): bool
+ {
+ return 1 === count($types)
+ && Type::BUILTIN_TYPE_OBJECT === $types[0]->getBuiltinType()
+ && is_a($types[0]->getClassName(), AbstractUid::class, true);
+ }
+}
diff --git a/tests/Functional/Controller/ApiController80.php b/tests/Functional/Controller/ApiController80.php
index ee4d05ff0..caab30d52 100644
--- a/tests/Functional/Controller/ApiController80.php
+++ b/tests/Functional/Controller/ApiController80.php
@@ -26,6 +26,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithNullableSchemaSet;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithObjectType;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithRef;
+use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithUuid;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\RangeInteger;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraints80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
@@ -414,6 +415,17 @@ public function entityWithObjectType()
{
}
+ /**
+ * @Route("/entity-with-uuid", methods={"GET", "POST"})
+ *
+ * @OA\Response(response=200, description="success", @OA\JsonContent(
+ * ref=@Model(type=EntityWithUuid::class),
+ * ))
+ */
+ public function entityWithUuid()
+ {
+ }
+
/**
* @Route("/form-with-alternate-type", methods={"POST"})
*
diff --git a/tests/Functional/Controller/ApiController81.php b/tests/Functional/Controller/ApiController81.php
index 3b80bce89..489c92ca4 100644
--- a/tests/Functional/Controller/ApiController81.php
+++ b/tests/Functional/Controller/ApiController81.php
@@ -27,6 +27,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithNullableSchemaSet;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithObjectType;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithRef;
+use Nelmio\ApiDocBundle\Tests\Functional\Entity\EntityWithUuid;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\QueryModel\ArrayQueryModel;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\QueryModel\FilterQueryModel;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\QueryModel\PaginationQueryModel;
@@ -345,6 +346,18 @@ public function entityWithObjectType()
{
}
+ #[Route('/entity-with-uuid', methods: ['GET', 'POST'])]
+ #[OA\Response(
+ response: 200,
+ description: 'success',
+ content: new OA\JsonContent(
+ ref: new Model(type: EntityWithUuid::class),
+ ),
+ )]
+ public function entityWithUuid()
+ {
+ }
+
#[Route('/form-with-alternate-type', methods: ['POST'])]
#[OA\Response(
response: 204,
diff --git a/tests/Functional/Entity/EntityWithUuid.php b/tests/Functional/Entity/EntityWithUuid.php
new file mode 100644
index 000000000..6515b95cb
--- /dev/null
+++ b/tests/Functional/Entity/EntityWithUuid.php
@@ -0,0 +1,26 @@
+id = Uuid::v1();
+ $this->name = $name;
+ }
+}
diff --git a/tests/Functional/FunctionalTest.php b/tests/Functional/FunctionalTest.php
index 88ff470ff..6a534e4b1 100644
--- a/tests/Functional/FunctionalTest.php
+++ b/tests/Functional/FunctionalTest.php
@@ -896,6 +896,24 @@ public function testEntitiesWithOverriddenSchemaTypeDoNotReadOtherProperties():
self::assertSame(Generator::UNDEFINED, $model->properties);
}
+ public function testEntityWithUuid(): void
+ {
+ self::assertEquals([
+ 'schema' => 'EntityWithUuid',
+ 'type' => 'object',
+ 'required' => ['id', 'name'],
+ 'properties' => [
+ 'id' => [
+ 'type' => 'string',
+ 'format' => 'uuid',
+ ],
+ 'name' => [
+ 'type' => 'string',
+ ],
+ ],
+ ], json_decode($this->getModel('EntityWithUuid')->toJson(), true));
+ }
+
public function testEntitiesWithRefInSchemaDoNoReadOtherProperties(): void
{
$model = $this->getModel('EntityWithRef');
diff --git a/tests/PropertyDescriber/UuidPropertyDescriberTest.php b/tests/PropertyDescriber/UuidPropertyDescriberTest.php
new file mode 100644
index 000000000..6b3d904c3
--- /dev/null
+++ b/tests/PropertyDescriber/UuidPropertyDescriberTest.php
@@ -0,0 +1,77 @@
+supports([$type]));
+ }
+
+ public function testSupportsNoIntPropertyType(): void
+ {
+ $type = new Type(Type::BUILTIN_TYPE_INT, false);
+
+ $describer = new UuidPropertyDescriber();
+
+ self::assertFalse($describer->supports([$type]));
+ }
+
+ public function testSupportsNoDifferentObjectPropertyType(): void
+ {
+ $type = new Type(Type::BUILTIN_TYPE_OBJECT, false, \DateTimeInterface::class);
+
+ $describer = new UuidPropertyDescriber();
+
+ self::assertFalse($describer->supports([$type]));
+ }
+
+ public function testDescribeUuidPropertyType(): void
+ {
+ $property = $this->initProperty();
+ $schema = $this->initSchema();
+
+ $describer = new UuidPropertyDescriber();
+ $describer->describe([], $property, [], $schema);
+
+ self::assertSame('string', $property->type);
+ self::assertSame('uuid', $property->format);
+ }
+
+ private function initProperty(): \OpenApi\Annotations\Property
+ {
+ if (PHP_VERSION_ID < 80000) {
+ return new \OpenApi\Annotations\Property([]);
+ }
+
+ return new \OpenApi\Attributes\Property(); // union types, used in schema attribute require PHP >= 8.0.0
+ }
+
+ private function initSchema(): \OpenApi\Annotations\Schema
+ {
+ if (PHP_VERSION_ID < 80000) {
+ return new \OpenApi\Annotations\Schema([]);
+ }
+
+ return new \OpenApi\Attributes\Schema(); // union types, used in schema attribute require PHP >= 8.0.0
+ }
+}