diff --git a/src/JsonApi/JsonSchema/SchemaFactory.php b/src/JsonApi/JsonSchema/SchemaFactory.php index 8d68a2f154a..adf7594219e 100644 --- a/src/JsonApi/JsonSchema/SchemaFactory.php +++ b/src/JsonApi/JsonSchema/SchemaFactory.php @@ -184,17 +184,21 @@ private function buildDefinitionPropertiesSchema(string $key, string $className, $relatedDefinitions = []; foreach ($properties as $propertyName => $property) { if ($relation = $this->getRelationship($className, $propertyName, $serializerContext)) { - [$isOne, $hasOperations, $relatedClassName] = $relation; - if (false === $hasOperations) { - continue; - } + [$isOne, $relatedClasses] = $relation; + $refs = []; + foreach ($relatedClasses as $relatedClassName => $hasOperations) { + if (false === $hasOperations) { + continue; + } - $operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext); - $inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext); - $serializerContext ??= $this->getSerializerContext($operation, $type); - $definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext); - $ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName; - $relatedDefinitions[$propertyName] = ['$ref' => $ref]; + $operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext); + $inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext); + $serializerContext ??= $this->getSerializerContext($operation, $type); + $definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext); + $ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName; + $refs[$ref] = '$ref'; + } + $relatedDefinitions[$propertyName] = array_flip($refs); if ($isOne) { $relationships[$propertyName]['properties']['data'] = self::RELATION_PROPS; continue; @@ -203,7 +207,6 @@ private function buildDefinitionPropertiesSchema(string $key, string $className, 'type' => 'array', 'items' => self::RELATION_PROPS, ]; - continue; } if ('id' === $propertyName) { $attributes['_id'] = $property; @@ -264,7 +267,7 @@ private function getRelationship(string $resourceClass, string $property, ?array $types = $propertyMetadata->getBuiltinTypes() ?? []; $isRelationship = false; $isOne = $isMany = false; - $className = $hasOperations = null; + $relatedClasses = []; foreach ($types as $type) { if ($type->isCollection()) { @@ -281,9 +284,9 @@ private function getRelationship(string $resourceClass, string $property, ?array $operation = $resourceMetadata->getOperation(); // @see https://github.com/api-platform/core/issues/5501 // @see https://github.com/api-platform/core/pull/5722 - $hasOperations ??= $operation->canRead(); + $relatedClasses[$className] = $operation->canRead(); } - return $isRelationship ? [$isOne, $hasOperations, $className] : null; + return $isRelationship ? [$isOne, $relatedClasses] : null; } } diff --git a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php index b7c33c76555..a55ad77d6f5 100644 --- a/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php +++ b/tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php @@ -124,9 +124,9 @@ public function testArraySchemaWithReference(): void ]); } - public function testArraySchemaWithMultipleUnionTypes(): void + public function testArraySchemaWithMultipleUnionTypesJsonLd(): void { - $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output']); + $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonld']); $result = $this->tester->getDisplay(); $json = json_decode($result, associative: true); @@ -140,6 +140,38 @@ public function testArraySchemaWithMultipleUnionTypes(): void $this->assertArrayHasKey('Robin.jsonld', $json['definitions']); } + public function testArraySchemaWithMultipleUnionTypesJsonApi(): void + { + $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonapi']); + $result = $this->tester->getDisplay(); + $json = json_decode($result, associative: true); + + $this->assertEquals($json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf'], [ + ['$ref' => '#/definitions/Wren.jsonapi'], + ['$ref' => '#/definitions/Robin.jsonapi'], + ['type' => 'null'], + ]); + + $this->assertArrayHasKey('Wren.jsonapi', $json['definitions']); + $this->assertArrayHasKey('Robin.jsonapi', $json['definitions']); + } + + public function testArraySchemaWithMultipleUnionTypesJsonHal(): void + { + $this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonhal']); + $result = $this->tester->getDisplay(); + $json = json_decode($result, associative: true); + + $this->assertEquals($json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf'], [ + ['$ref' => '#/definitions/Wren.jsonhal'], + ['$ref' => '#/definitions/Robin.jsonhal'], + ['type' => 'null'], + ]); + + $this->assertArrayHasKey('Wren.jsonhal', $json['definitions']); + $this->assertArrayHasKey('Robin.jsonhal', $json['definitions']); + } + /** * TODO: add deprecation (TypeFactory will be deprecated in api platform 3.3). *