Skip to content

Commit bfdea8c

Browse files
fix(jsonschema): don't skip remaining multiple union types
1 parent 1cc114b commit bfdea8c

File tree

6 files changed

+175
-8
lines changed

6 files changed

+175
-8
lines changed

src/JsonSchema/SchemaFactory.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,13 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
196196
// property schema is created in SchemaPropertyMetadataFactory, but it cannot build resource reference ($ref)
197197
// complete property schema with resource reference ($ref) only if it's related to an object
198198
$version = $schema->getVersion();
199-
$subSchema = new Schema($version);
200-
$subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema
199+
$refs = [];
200+
$isNullable = null;
201201

202202
foreach ($types as $type) {
203+
$subSchema = new Schema($version);
204+
$subSchema->setDefinitions($schema->getDefinitions()); // Populate definitions of the main schema
205+
203206
// TODO: in 3.3 add trigger_deprecation() as type factories are not used anymore, we moved this logic to SchemaPropertyMetadataFactory so that it gets cached
204207
if ($typeFromFactory = $this->typeFactory?->getType($type, 'jsonschema', $propertyMetadata->isReadableLink(), $serializerContext)) {
205208
$propertySchema = $typeFromFactory;
@@ -230,14 +233,20 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
230233
break;
231234
}
232235

233-
if ($type->isNullable()) {
234-
$propertySchema['anyOf'] = [['$ref' => $subSchema['$ref']], ['type' => 'null']];
235-
} else {
236-
$propertySchema['$ref'] = $subSchema['$ref'];
237-
}
236+
$refs[] = ['$ref' => $subSchema['$ref']];
237+
$isNullable = $isNullable ?? $type->isNullable();
238+
}
238239

240+
if ($isNullable) {
241+
$refs[] = ['type' => 'null'];
242+
}
243+
244+
if (($c = \count($refs)) > 1) {
245+
$propertySchema['anyOf'] = $refs;
246+
unset($propertySchema['type']);
247+
} elseif (1 === $c) {
248+
$propertySchema['$ref'] = $refs[0]['$ref'];
239249
unset($propertySchema['type']);
240-
break;
241250
}
242251

243252
$schema->getDefinitions()[$definitionName]['properties'][$normalizedPropertyName] = new \ArrayObject($propertySchema);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212;
15+
16+
interface Bird extends \JsonSerializable
17+
{
18+
public function getName(): ?string;
19+
20+
public function getAge(): ?int;
21+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212;
15+
16+
final class Robin implements Bird
17+
{
18+
public ?string $name = null;
19+
public ?int $age = null;
20+
21+
public function getName(): ?string
22+
{
23+
return $this->name;
24+
}
25+
26+
public function getAge(): ?int
27+
{
28+
return $this->age;
29+
}
30+
31+
public function jsonSerialize(): array
32+
{
33+
return get_object_vars($this);
34+
}
35+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212;
15+
16+
final class Wren implements Bird
17+
{
18+
public ?string $name = null;
19+
public ?int $age = null;
20+
public ?int $weight = null;
21+
22+
public function getName(): ?string
23+
{
24+
return $this->name;
25+
}
26+
27+
public function getAge(): ?int
28+
{
29+
return $this->age;
30+
}
31+
32+
public function jsonSerialize(): array
33+
{
34+
return get_object_vars($this);
35+
}
36+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Bird;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Robin;
19+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6212\Wren;
20+
use Doctrine\ORM\Mapping as ORM;
21+
22+
#[ApiResource]
23+
#[ORM\Entity]
24+
class Nest
25+
{
26+
#[ORM\Id]
27+
#[ORM\GeneratedValue]
28+
#[ORM\Column]
29+
private ?int $id = null;
30+
31+
#[ORM\Column(type: 'json')]
32+
private ?Bird $owner;
33+
34+
public function getId(): ?int
35+
{
36+
return $this->id;
37+
}
38+
39+
public function getOwner(): ?Bird
40+
{
41+
return $this->owner;
42+
}
43+
44+
public function setOwner(Wren|Robin|null $owner): static
45+
{
46+
$this->owner = $owner;
47+
48+
return $this;
49+
}
50+
}

tests/JsonSchema/Command/JsonSchemaGenerateCommandTest.php

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

127+
public function testArraySchemaWithMultipleUnionTypes(): void
128+
{
129+
$this->tester->run(['command' => 'api:json-schema:generate', 'resource' => 'ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6212\Nest', '--type' => 'output']);
130+
$result = $this->tester->getDisplay();
131+
$json = json_decode($result, associative: true);
132+
133+
$this->assertEquals($json['definitions']['Nest.jsonld']['properties']['owner']['anyOf'], [
134+
['$ref' => '#/definitions/Wren.jsonld'],
135+
['$ref' => '#/definitions/Robin.jsonld'],
136+
['type' => 'null'],
137+
]);
138+
139+
$this->assertArrayHasKey('Wren.jsonld', $json['definitions']);
140+
$this->assertArrayHasKey('Robin.jsonld', $json['definitions']);
141+
}
142+
127143
/**
128144
* TODO: add deprecation (TypeFactory will be deprecated in api platform 3.3).
129145
*

0 commit comments

Comments
 (0)