fix(jsonschema): add support for normalization/denormalization with a… · api-platform/core@5b4e851 · GitHub
Skip to content

Commit 5b4e851

Browse files
committed
fix(jsonschema): add support for normalization/denormalization with attributes
1 parent 6e83dd9 commit 5b4e851

11 files changed

Lines changed: 650 additions & 21 deletions

File tree

‎src/JsonSchema/DefinitionNameFactory.php‎

Lines changed: 35 additions & 1 deletion

‎src/JsonSchema/SchemaFactory.php‎

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,24 @@ private function buildLegacyPropertySchema(Schema $schema, string $definitionNam
244244
continue;
245245
}
246246

247+
$childSerializerContext = $serializerContext + [self::FORCE_SUBSCHEMA => true, 'gen_id' => $propertyMetadata->getGenId() ?? true];
248+
if (isset($serializerContext[AbstractNormalizer::ATTRIBUTES])) {
249+
$attributes = $serializerContext[AbstractNormalizer::ATTRIBUTES];
250+
if (\is_array($attributes) && \array_key_exists($normalizedPropertyName, $attributes) && \is_array($attributes[$normalizedPropertyName])) {
251+
$childSerializerContext[AbstractNormalizer::ATTRIBUTES] = $attributes[$normalizedPropertyName];
252+
} else {
253+
unset($childSerializerContext[AbstractNormalizer::ATTRIBUTES]);
254+
}
255+
}
256+
247257
$subSchemaFactory = $this->schemaFactory ?: $this;
248258
$subSchema = $subSchemaFactory->buildSchema(
249259
$className,
250260
$format,
251261
$parentType,
252262
null,
253263
$subSchema,
254-
$serializerContext + [self::FORCE_SUBSCHEMA => true, 'gen_id' => $propertyMetadata->getGenId() ?? true],
264+
$childSerializerContext,
255265
false,
256266
);
257267

@@ -365,6 +375,16 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
365375
$className = $valueType->getWrappedType()->getClassName();
366376
}
367377

378+
$childSerializerContext = $serializerContext + [self::FORCE_SUBSCHEMA => true, 'gen_id' => $propertyMetadata->getGenId() ?? true];
379+
if (isset($serializerContext[AbstractNormalizer::ATTRIBUTES])) {
380+
$attributes = $serializerContext[AbstractNormalizer::ATTRIBUTES];
381+
if (\is_array($attributes) && \array_key_exists($normalizedPropertyName, $attributes) && \is_array($attributes[$normalizedPropertyName])) {
382+
$childSerializerContext[AbstractNormalizer::ATTRIBUTES] = $attributes[$normalizedPropertyName];
383+
} else {
384+
unset($childSerializerContext[AbstractNormalizer::ATTRIBUTES]);
385+
}
386+
}
387+
368388
$subSchemaInstance = new Schema($version);
369389
$subSchemaInstance->setDefinitions($schema->getDefinitions());
370390
$subSchemaFactory = $this->schemaFactory ?: $this;
@@ -374,7 +394,7 @@ private function buildPropertySchema(Schema $schema, string $definitionName, str
374394
$parentType,
375395
null,
376396
$subSchemaInstance,
377-
$serializerContext + [self::FORCE_SUBSCHEMA => true, 'gen_id' => $propertyMetadata->getGenId() ?? true],
397+
$childSerializerContext,
378398
false,
379399
);
380400
if (!isset($subSchemaResult['$ref'])) {
@@ -448,6 +468,18 @@ private function getFactoryOptions(array $serializerContext, array $validationGr
448468
$options['denormalization_groups'] = $denormalizationGroups;
449469
}
450470

471+
if (isset($serializerContext[AbstractNormalizer::ATTRIBUTES])) {
472+
$options['serializer_attributes'] = (array) $serializerContext[AbstractNormalizer::ATTRIBUTES];
473+
}
474+
475+
if ($operation && ($normalizationAttributes = $operation->getNormalizationContext()['attributes'] ?? null)) {
476+
$options['normalization_attributes'] = $normalizationAttributes;
477+
}
478+
479+
if ($operation && ($denormalizationAttributes = $operation->getDenormalizationContext()['attributes'] ?? null)) {
480+
$options['denormalization_attributes'] = $denormalizationAttributes;
481+
}
482+
451483
if ($validationGroups) {
452484
$options['validation_groups'] = $validationGroups;
453485
}

‎src/JsonSchema/Tests/DefinitionNameFactoryTest.php‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,46 @@ public static function providerDefinitions(): iterable
7070
yield ['Bar.DtoOutput.jsonapi-read_write', Dummy::class, 'jsonapi', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::GROUPS => ['read', 'write']]];
7171
yield ['Bar.DtoOutput.jsonhal-read_write', Dummy::class, 'jsonhal', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::GROUPS => ['read', 'write']]];
7272
yield ['Bar.DtoOutput.jsonld-read_write', Dummy::class, 'jsonld', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::GROUPS => ['read', 'write']]];
73+
74+
yield ['Dummy-id', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['id']]];
75+
yield ['Dummy.jsonapi-id', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['id']]];
76+
yield ['Dummy.jsonhal-id', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['id']]];
77+
yield ['Dummy.jsonld-id', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['id']]];
78+
79+
yield ['Dummy-id_name', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['id', 'name']]];
80+
yield ['Dummy.jsonapi-id_name', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['id', 'name']]];
81+
yield ['Dummy.jsonhal-id_name', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['id', 'name']]];
82+
yield ['Dummy.jsonld-id_name', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['id', 'name']]];
83+
84+
yield ['Dummy-title_author.name', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name']]]];
85+
yield ['Dummy.jsonapi-title_author.name', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name']]]];
86+
yield ['Dummy.jsonhal-title_author.name', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name']]]];
87+
yield ['Dummy.jsonld-title_author.name', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name']]]];
88+
89+
yield ['Dummy-title_author', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => 'name']]];
90+
yield ['Dummy.jsonapi-title_author', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => 'name']]];
91+
yield ['Dummy.jsonhal-title_author', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => 'name']]];
92+
yield ['Dummy.jsonld-title_author', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => 'name']]];
93+
94+
yield ['Dummy-title_author_name', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author', 'name']]];
95+
yield ['Dummy.jsonapi-title_author_name', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author', 'name']]];
96+
yield ['Dummy.jsonhal-title_author_name', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author', 'name']]];
97+
yield ['Dummy.jsonld-title_author_name', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author', 'name']]];
98+
99+
yield ['Dummy-title_author.name_name', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
100+
yield ['Dummy.jsonapi-title_author.name_name', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
101+
yield ['Dummy.jsonhal-title_author.name_name', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
102+
yield ['Dummy.jsonld-title_author.name_name', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
103+
104+
yield ['Dummy-title_author.name_author.id_name', Dummy::class, 'json', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name', 'id'], 'name']]];
105+
yield ['Dummy.jsonapi-title_author.name_author.id_name', Dummy::class, 'jsonapi', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name', 'id'], 'name']]];
106+
yield ['Dummy.jsonhal-title_author.name_author.id_name', Dummy::class, 'jsonhal', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name', 'id'], 'name']]];
107+
yield ['Dummy.jsonld-title_author.name_author.id_name', Dummy::class, 'jsonld', null, null, [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name', 'id'], 'name']]];
108+
109+
yield ['Bar.DtoOutput-title_author.name_name', Dummy::class, 'json', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
110+
yield ['Bar.DtoOutput.jsonapi-title_author.name_name', Dummy::class, 'jsonapi', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
111+
yield ['Bar.DtoOutput.jsonhal-title_author.name_name', Dummy::class, 'jsonhal', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
112+
yield ['Bar.DtoOutput.jsonld-title_author.name_name', Dummy::class, 'jsonld', DtoOutput::class, new Get(shortName: 'Bar'), [AbstractNormalizer::ATTRIBUTES => ['title', 'author' => ['name'], 'name']]];
73113
}
74114

75115
#[\PHPUnit\Framework\Attributes\DataProvider('providerDefinitions')]
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
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\JsonSchema\Tests\Fixtures\ApiResource;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use Doctrine\ORM\Mapping as ORM;
18+
19+
#[ORM\Entity]
20+
#[ApiResource]
21+
class ChildAttributeDummy
22+
{
23+
#[ORM\Id]
24+
#[ORM\GeneratedValue]
25+
#[ORM\Column]
26+
private ?int $id = null;
27+
28+
#[ORM\Column(length: 255)]
29+
private ?string $name = null;
30+
31+
#[ORM\Column(length: 50)]
32+
private ?string $code = null;
33+
34+
public function getId(): ?int
35+
{
36+
return $this->id;
37+
}
38+
39+
public function getName(): ?string
40+
{
41+
return $this->name;
42+
}
43+
44+
public function setName(string $name): self
45+
{
46+
$this->name = $name;
47+
48+
return $this;
49+
}
50+
51+
public function getCode(): ?string
52+
{
53+
return $this->code;
54+
}
55+
56+
public function setCode(string $code): self
57+
{
58+
$this->code = $code;
59+
60+
return $this;
61+
}
62+
}
Lines changed: 84 additions & 0 deletions

0 commit comments

Comments
 (0)