Skip to content

Commit 97c8ae2

Browse files
fix(jsonapi): handle multiple relation classes, unrelated unions (#6320)
1 parent 629da78 commit 97c8ae2

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

src/JsonApi/JsonSchema/SchemaFactory.php

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,21 @@ private function buildDefinitionPropertiesSchema(string $key, string $className,
184184
$relatedDefinitions = [];
185185
foreach ($properties as $propertyName => $property) {
186186
if ($relation = $this->getRelationship($className, $propertyName, $serializerContext)) {
187-
[$isOne, $hasOperations, $relatedClassName] = $relation;
188-
if (false === $hasOperations) {
189-
continue;
190-
}
187+
[$isOne, $relatedClasses] = $relation;
188+
$refs = [];
189+
foreach ($relatedClasses as $relatedClassName => $hasOperations) {
190+
if (false === $hasOperations) {
191+
continue;
192+
}
191193

192-
$operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext);
193-
$inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext);
194-
$serializerContext ??= $this->getSerializerContext($operation, $type);
195-
$definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext);
196-
$ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
197-
$relatedDefinitions[$propertyName] = ['$ref' => $ref];
194+
$operation = $this->findOperation($relatedClassName, $type, $operation, $serializerContext);
195+
$inputOrOutputClass = $this->findOutputClass($relatedClassName, $type, $operation, $serializerContext);
196+
$serializerContext ??= $this->getSerializerContext($operation, $type);
197+
$definitionName = $this->definitionNameFactory->create($relatedClassName, $format, $inputOrOutputClass, $operation, $serializerContext);
198+
$ref = Schema::VERSION_OPENAPI === $schema->getVersion() ? '#/components/schemas/'.$definitionName : '#/definitions/'.$definitionName;
199+
$refs[$ref] = '$ref';
200+
}
201+
$relatedDefinitions[$propertyName] = array_flip($refs);
198202
if ($isOne) {
199203
$relationships[$propertyName]['properties']['data'] = self::RELATION_PROPS;
200204
continue;
@@ -203,7 +207,6 @@ private function buildDefinitionPropertiesSchema(string $key, string $className,
203207
'type' => 'array',
204208
'items' => self::RELATION_PROPS,
205209
];
206-
continue;
207210
}
208211
if ('id' === $propertyName) {
209212
$attributes['_id'] = $property;
@@ -264,7 +267,7 @@ private function getRelationship(string $resourceClass, string $property, ?array
264267
$types = $propertyMetadata->getBuiltinTypes() ?? [];
265268
$isRelationship = false;
266269
$isOne = $isMany = false;
267-
$className = $hasOperations = null;
270+
$relatedClasses = [];
268271

269272
foreach ($types as $type) {
270273
if ($type->isCollection()) {
@@ -281,9 +284,9 @@ private function getRelationship(string $resourceClass, string $property, ?array
281284
$operation = $resourceMetadata->getOperation();
282285
// @see https://github.com/api-platform/core/issues/5501
283286
// @see https://github.com/api-platform/core/pull/5722
284-
$hasOperations ??= $operation->canRead();
287+
$relatedClasses[$className] = $operation->canRead();
285288
}
286289

287-
return $isRelationship ? [$isOne, $hasOperations, $className] : null;
290+
return $isRelationship ? [$isOne, $relatedClasses] : null;
288291
}
289292
}

tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,9 @@ public function testArraySchemaWithReference(): void
124124
]);
125125
}
126126

127-
public function testArraySchemaWithMultipleUnionTypes(): void
127+
public function testArraySchemaWithMultipleUnionTypesJsonLd(): void
128128
{
129-
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output']);
129+
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonld']);
130130
$result = $this->tester->getDisplay();
131131
$json = json_decode($result, associative: true);
132132

@@ -140,6 +140,38 @@ public function testArraySchemaWithMultipleUnionTypes(): void
140140
$this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
141141
}
142142

143+
public function testArraySchemaWithMultipleUnionTypesJsonApi(): void
144+
{
145+
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonapi']);
146+
$result = $this->tester->getDisplay();
147+
$json = json_decode($result, associative: true);
148+
149+
$this->assertEquals($json['definitions']['Nest.jsonapi']['properties']['data']['properties']['attributes']['properties']['owner']['anyOf'], [
150+
['$ref' => '#/definitions/Wren.jsonapi'],
151+
['$ref' => '#/definitions/Robin.jsonapi'],
152+
['type' => 'null'],
153+
]);
154+
155+
$this->assertArrayHasKey('Wren.jsonapi', $json['definitions']);
156+
$this->assertArrayHasKey('Robin.jsonapi', $json['definitions']);
157+
}
158+
159+
public function testArraySchemaWithMultipleUnionTypesJsonHal(): void
160+
{
161+
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output', '--format' => 'jsonhal']);
162+
$result = $this->tester->getDisplay();
163+
$json = json_decode($result, associative: true);
164+
165+
$this->assertEquals($json['definitions']['Nest.jsonhal']['properties']['owner']['anyOf'], [
166+
['$ref' => '#/definitions/Wren.jsonhal'],
167+
['$ref' => '#/definitions/Robin.jsonhal'],
168+
['type' => 'null'],
169+
]);
170+
171+
$this->assertArrayHasKey('Wren.jsonhal', $json['definitions']);
172+
$this->assertArrayHasKey('Robin.jsonhal', $json['definitions']);
173+
}
174+
143175
/**
144176
* TODO: add deprecation (TypeFactory will be deprecated in api platform 3.3).
145177
*

0 commit comments

Comments
 (0)