diff --git a/src/JsonSchema/SchemaFactory.php b/src/JsonSchema/SchemaFactory.php index a1ab3610e0b..b518c308c1b 100644 --- a/src/JsonSchema/SchemaFactory.php +++ b/src/JsonSchema/SchemaFactory.php @@ -80,7 +80,7 @@ public function buildSchema(string $className, string $format = 'json', string $ } $version = $schema->getVersion(); - $definitionName = $this->buildDefinitionName($className, $format, $type, $operationType, $operationName, $serializerContext); + $definitionName = $this->buildDefinitionName($className, $format, $inputOrOutputClass, $resourceMetadata, $serializerContext); if (null === $operationType || null === $operationName) { $method = Schema::TYPE_INPUT === $type ? 'POST' : 'GET'; @@ -239,10 +239,8 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str $schema->getDefinitions()[$definitionName]['properties'][$normalizedPropertyName] = $propertySchema; } - private function buildDefinitionName(string $className, string $format = 'json', string $type = Schema::TYPE_OUTPUT, ?string $operationType = null, ?string $operationName = null, ?array $serializerContext = null): string + private function buildDefinitionName(string $className, string $format = 'json', ?string $inputOrOutputClass = null, ?ResourceMetadata $resourceMetadata = null, ?array $serializerContext = null): string { - [$resourceMetadata, $serializerContext,, $inputOrOutputClass] = $this->getMetadata($className, $type, $operationType, $operationName, $serializerContext); - $prefix = $resourceMetadata ? $resourceMetadata->getShortName() : (new \ReflectionClass($className))->getShortName(); if (null !== $inputOrOutputClass && $className !== $inputOrOutputClass) { $parts = explode('\\', $inputOrOutputClass); diff --git a/src/JsonSchema/TypeFactory.php b/src/JsonSchema/TypeFactory.php index 04e71d4e60f..8979534395c 100644 --- a/src/JsonSchema/TypeFactory.php +++ b/src/JsonSchema/TypeFactory.php @@ -142,7 +142,7 @@ private function getClassType(?string $className, string $format, ?bool $readabl throw new \LogicException('The schema factory must be injected by calling the "setSchemaFactory" method.'); } - $subSchema = $this->schemaFactory->buildSchema($className, $format, Schema::TYPE_OUTPUT, null, null, $subSchema, $serializerContext); + $subSchema = $this->schemaFactory->buildSchema($className, $format, Schema::TYPE_OUTPUT, null, null, $subSchema, $serializerContext, false); return ['$ref' => $subSchema['$ref']]; } diff --git a/src/OpenApi/Factory/OpenApiFactory.php b/src/OpenApi/Factory/OpenApiFactory.php index 9f0446e4acb..db18623e182 100644 --- a/src/OpenApi/Factory/OpenApiFactory.php +++ b/src/OpenApi/Factory/OpenApiFactory.php @@ -86,7 +86,7 @@ public function __invoke(array $context = []): OpenApi $servers = '/' === $baseUrl || '' === $baseUrl ? [new Model\Server('/')] : [new Model\Server($baseUrl)]; $paths = new Model\Paths(); $links = []; - $schemas = []; + $schemas = new \ArrayObject(); foreach ($this->resourceNameCollectionFactory->create() as $resourceClass) { $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass); @@ -94,11 +94,11 @@ public function __invoke(array $context = []): OpenApi // Items needs to be parsed first to be able to reference the lines from the collection operation [$itemOperationLinks, $itemOperationSchemas] = $this->collectPaths($resourceMetadata, $resourceClass, OperationType::ITEM, $context, $paths, $links, $schemas); - $schemas += $itemOperationSchemas; + $this->appendSchemaDefinitions($schemas, $itemOperationSchemas); [$collectionOperationLinks, $collectionOperationSchemas] = $this->collectPaths($resourceMetadata, $resourceClass, OperationType::COLLECTION, $context, $paths, $links, $schemas); [$subresourceOperationLinks, $subresourceOperationSchemas] = $this->collectPaths($resourceMetadata, $resourceClass, OperationType::SUBRESOURCE, $context, $paths, $links, $schemas); - $schemas += $collectionOperationSchemas; + $this->appendSchemaDefinitions($schemas, $collectionOperationSchemas); } $securitySchemes = $this->getSecuritySchemes(); @@ -113,7 +113,7 @@ public function __invoke(array $context = []): OpenApi $servers, $paths, new Model\Components( - new \ArrayObject($schemas), + $schemas, new \ArrayObject(), new \ArrayObject(), new \ArrayObject(), @@ -128,7 +128,7 @@ public function __invoke(array $context = []): OpenApi /** * @return array | array */ - private function collectPaths(ResourceMetadata $resourceMetadata, string $resourceClass, string $operationType, array $context, Model\Paths $paths, array &$links, array $schemas = []): array + private function collectPaths(ResourceMetadata $resourceMetadata, string $resourceClass, string $operationType, array $context, Model\Paths $paths, array &$links, \ArrayObject $schemas): array { $resourceShortName = $resourceMetadata->getShortName(); $operations = OperationType::COLLECTION === $operationType ? $resourceMetadata->getCollectionOperations() : (OperationType::ITEM === $operationType ? $resourceMetadata->getItemOperations() : $this->subresourceOperationFactory->create($resourceClass)); @@ -155,12 +155,14 @@ private function collectPaths(ResourceMetadata $resourceMetadata, string $resour $linkedOperationId = 'get'.ucfirst($resourceShortName).ucfirst(OperationType::ITEM); $pathItem = $paths->getPath($path) ?: new Model\PathItem(); $forceSchemaCollection = OperationType::SUBRESOURCE === $operationType ? ($operation['collection'] ?? false) : false; + $schema = new Schema('openapi'); + $schema->setDefinitions($schemas); $operationOutputSchemas = []; foreach ($responseMimeTypes as $operationFormat) { - $operationOutputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_OUTPUT, $operationType, $operationName, new Schema('openapi'), null, $forceSchemaCollection); - $schemas += $operationOutputSchema->getDefinitions()->getArrayCopy(); + $operationOutputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_OUTPUT, $operationType, $operationName, $schema, null, $forceSchemaCollection); $operationOutputSchemas[$operationFormat] = $operationOutputSchema; + $this->appendSchemaDefinitions($schemas, $operationOutputSchema->getDefinitions()); } $parameters = []; @@ -264,9 +266,9 @@ private function collectPaths(ResourceMetadata $resourceMetadata, string $resour } elseif ('PUT' === $method || 'POST' === $method || 'PATCH' === $method) { $operationInputSchemas = []; foreach ($requestMimeTypes as $operationFormat) { - $operationInputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_INPUT, $operationType, $operationName, new Schema('openapi'), null, $forceSchemaCollection); - $schemas += $operationInputSchema->getDefinitions()->getArrayCopy(); + $operationInputSchema = $this->jsonSchemaFactory->buildSchema($resourceClass, $operationFormat, Schema::TYPE_INPUT, $operationType, $operationName, $schema, null, $forceSchemaCollection); $operationInputSchemas[$operationFormat] = $operationInputSchema; + $this->appendSchemaDefinitions($schemas, $operationInputSchema->getDefinitions()); } $requestBody = new Model\RequestBody(sprintf('The %s %s resource', 'POST' === $method ? 'new' : 'updated', $resourceShortName), $this->buildContent($requestMimeTypes, $operationInputSchemas), true); @@ -510,6 +512,13 @@ private function getSecuritySchemes(): array return $securitySchemes; } + private function appendSchemaDefinitions(\ArrayObject &$schemas, \ArrayObject $definitions): void + { + foreach ($definitions as $key => $value) { + $schemas[$key] = $value; + } + } + /** * @var Model\Parameter[] */ diff --git a/tests/JsonSchema/TypeFactoryTest.php b/tests/JsonSchema/TypeFactoryTest.php index 03ea3409d31..0a953ec860c 100644 --- a/tests/JsonSchema/TypeFactoryTest.php +++ b/tests/JsonSchema/TypeFactoryTest.php @@ -372,7 +372,7 @@ public function testGetClassType(): void { $schemaFactoryProphecy = $this->prophesize(SchemaFactoryInterface::class); - $schemaFactoryProphecy->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_OUTPUT, null, null, Argument::type(Schema::class), ['foo' => 'bar'])->will(function (array $args) { + $schemaFactoryProphecy->buildSchema(Dummy::class, 'jsonld', Schema::TYPE_OUTPUT, null, null, Argument::type(Schema::class), ['foo' => 'bar'], false)->will(function (array $args) { $args[5]['$ref'] = 'ref'; return $args[5];