Skip to content

Commit 8b92034

Browse files
authored
Merge pull request #2921 from teohhanhui/fix/json-serializable-item-should-not-turn-into-collection
Fix normalizer priorities for correct JsonSerializable handling
2 parents d557f71 + 3c0c835 commit 8b92034

File tree

14 files changed

+697
-24
lines changed

14 files changed

+697
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.log
12
/.php_cs
23
/.php_cs.cache
34
/.phpunit.result.cache
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
Feature: JSON-LD using JsonSerializable types
2+
In order to use JsonSerializable in resource and non-resource types
3+
As a developer
4+
I should be able to serialize objects of JsonSerializable type.
5+
6+
Background:
7+
Given I add "Accept" header equal to "application/ld+json"
8+
And I add "Content-Type" header equal to "application/ld+json"
9+
10+
@createSchema
11+
Scenario: Create a Content
12+
When I send a "POST" request to "/contents" with body:
13+
"""
14+
{
15+
"contentType": "homepage",
16+
"fields": [
17+
{
18+
"name": "title",
19+
"value": "Labore reprehenderit dolorem repellendus asperiores."
20+
},
21+
{
22+
"name": "content",
23+
"value": "Minus sed repellendus corporis nemo. Aut aut veniam at aut aliquid. Architecto tempora quia neque numquam voluptas sint est delectus.\n\nUnde voluptatem animi non ut aut dicta. Omnis vero dolorum aliquid laudantium magni asperiores. Et tempora eveniet soluta modi occaecati.\n\nEa dolorum tenetur voluptatum temporibus illo fuga. Quibusdam et doloribus debitis omnis sed. Tempora in aperiam ullam non odit. Praesentium sunt accusantium dolorem commodi labore eum nostrum quia."
24+
}
25+
]
26+
}
27+
"""
28+
Then the response status code should be 201
29+
And the response should be in JSON
30+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
31+
And the JSON should be equal to:
32+
"""
33+
{
34+
"@context": "/contexts/Content",
35+
"@id": "/contents/1",
36+
"@type": "Content",
37+
"id": 1,
38+
"contentType": "homepage",
39+
"status": {
40+
"key": "DRAFT",
41+
"value": "draft"
42+
},
43+
"fieldValues": {
44+
"title": "Labore reprehenderit dolorem repellendus asperiores.",
45+
"content": "Minus sed repellendus corporis nemo. Aut aut veniam at aut aliquid. Architecto tempora quia neque numquam voluptas sint est delectus.\n\nUnde voluptatem animi non ut aut dicta. Omnis vero dolorum aliquid laudantium magni asperiores. Et tempora eveniet soluta modi occaecati.\n\nEa dolorum tenetur voluptatum temporibus illo fuga. Quibusdam et doloribus debitis omnis sed. Tempora in aperiam ullam non odit. Praesentium sunt accusantium dolorem commodi labore eum nostrum quia."
46+
}
47+
}
48+
"""
49+
50+
Scenario: Retrieve a Content
51+
When I send a "GET" request to "/contents/1"
52+
Then the response status code should be 200
53+
And the response should be in JSON
54+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
55+
And the JSON should be equal to:
56+
"""
57+
{
58+
"@context": "/contexts/Content",
59+
"@id": "/contents/1",
60+
"@type": "Content",
61+
"id": 1,
62+
"contentType": "homepage",
63+
"status": {
64+
"key": "DRAFT",
65+
"value": "draft"
66+
},
67+
"fieldValues": {
68+
"title": "Labore reprehenderit dolorem repellendus asperiores.",
69+
"content": "Minus sed repellendus corporis nemo. Aut aut veniam at aut aliquid. Architecto tempora quia neque numquam voluptas sint est delectus.\n\nUnde voluptatem animi non ut aut dicta. Omnis vero dolorum aliquid laudantium magni asperiores. Et tempora eveniet soluta modi occaecati.\n\nEa dolorum tenetur voluptatum temporibus illo fuga. Quibusdam et doloribus debitis omnis sed. Tempora in aperiam ullam non odit. Praesentium sunt accusantium dolorem commodi labore eum nostrum quia."
70+
}
71+
}
72+
"""

src/Bridge/Symfony/Bundle/Resources/config/api.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@
110110
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="ignore" />
111111
<argument>false</argument>
112112

113-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
114-
<tag name="serializer.normalizer" priority="-923" />
113+
<!-- Run before serializer.normalizer.json_serializable -->
114+
<tag name="serializer.normalizer" priority="-895" />
115115
</service>
116116

117117
<!-- Resources Operations path resolver -->

src/Bridge/Symfony/Bundle/Resources/config/elasticsearch.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@
5656
<argument type="service" id="property_info" on-invalid="ignore" />
5757
<argument type="service" id="serializer.mapping.class_discriminator_resolver" on-invalid="ignore" />
5858

59-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
60-
<tag name="serializer.normalizer" priority="-922" />
59+
<!-- Run before serializer.normalizer.json_serializable -->
60+
<tag name="serializer.normalizer" priority="-890" />
6161
</service>
6262

6363
<service id="api_platform.elasticsearch.item_data_provider" class="ApiPlatform\Core\Bridge\Elasticsearch\DataProvider\ItemDataProvider" public="false">

src/Bridge/Symfony/Bundle/Resources/config/graphql.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,17 @@
135135
<argument type="tagged" tag="api_platform.data_transformer" on-invalid="ignore" />
136136
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="ignore" />
137137

138-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
139-
<tag name="serializer.normalizer" priority="-922" />
138+
<!-- Run before serializer.normalizer.json_serializable -->
139+
<tag name="serializer.normalizer" priority="-890" />
140140
</service>
141141

142142
<service id="api_platform.graphql.normalizer.object" class="ApiPlatform\Core\GraphQl\Serializer\ObjectNormalizer" public="false">
143143
<argument type="service" id="serializer.normalizer.object" />
144144
<argument type="service" id="api_platform.iri_converter" />
145145
<argument type="service" id="api_platform.identifiers_extractor.cached" />
146146

147-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
148-
<tag name="serializer.normalizer" priority="-924" />
147+
<!-- Run after serializer.denormalizer.array but before serializer.normalizer.object -->
148+
<tag name="serializer.normalizer" priority="-995" />
149149
</service>
150150

151151
<!-- Command -->

src/Bridge/Symfony/Bundle/Resources/config/hal.xml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
<argument type="service" id="api_platform.resource_class_resolver" />
2424
<argument>%api_platform.collection.pagination.page_parameter_name%</argument>
2525

26-
<tag name="serializer.normalizer" priority="-790" />
26+
<!-- Run after api_platform.hal.normalizer.object but before serializer.normalizer.object and serializer.denormalizer.array -->
27+
<tag name="serializer.normalizer" priority="-985" />
2728
</service>
2829

2930
<service id="api_platform.hal.normalizer.item" class="ApiPlatform\Core\Hal\Serializer\ItemNormalizer" public="false">
@@ -41,16 +42,16 @@
4142
<argument type="service" id="api_platform.metadata.resource.metadata_factory" on-invalid="ignore" />
4243
<argument>false</argument>
4344

44-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
45-
<tag name="serializer.normalizer" priority="-922" />
45+
<!-- Run before serializer.normalizer.json_serializable -->
46+
<tag name="serializer.normalizer" priority="-890" />
4647
</service>
4748

4849
<service id="api_platform.hal.normalizer.object" class="ApiPlatform\Core\Hal\Serializer\ObjectNormalizer" public="false">
4950
<argument type="service" id="serializer.normalizer.object" />
5051
<argument type="service" id="api_platform.iri_converter" />
5152

52-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
53-
<tag name="serializer.normalizer" priority="-924" />
53+
<!-- Run after serializer.denormalizer.array but before serializer.normalizer.object -->
54+
<tag name="serializer.normalizer" priority="-995" />
5455
</service>
5556
</services>
5657

src/Bridge/Symfony/Bundle/Resources/config/hydra.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@
5757
<argument type="service" id="api_platform.resource_class_resolver" />
5858
<argument type="service" id="api_platform.iri_converter" />
5959

60-
<tag name="serializer.normalizer" priority="-790" />
60+
<!-- Run after api_platform.jsonld.normalizer.object but before serializer.normalizer.object and serializer.denormalizer.array -->
61+
<tag name="serializer.normalizer" priority="-985" />
6162
</service>
6263

6364
<service id="api_platform.hydra.normalizer.partial_collection_view" class="ApiPlatform\Core\Hydra\Serializer\PartialCollectionViewNormalizer" decorates="api_platform.hydra.normalizer.collection" public="false">

src/Bridge/Symfony/Bundle/Resources/config/jsonapi.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
<argument type="service" id="api_platform.resource_class_resolver" />
2828
<argument>%api_platform.collection.pagination.page_parameter_name%</argument>
2929

30-
31-
<tag name="serializer.normalizer" priority="-790" />
30+
<!-- Run after api_platform.jsonapi.normalizer.object but before serializer.normalizer.object and serializer.denormalizer.array -->
31+
<tag name="serializer.normalizer" priority="-985" />
3232
</service>
3333

3434
<service id="api_platform.jsonapi.normalizer.item" class="ApiPlatform\Core\JsonApi\Serializer\ItemNormalizer" public="false">
@@ -43,8 +43,8 @@
4343
<argument type="tagged" tag="api_platform.data_transformer" on-invalid="ignore" />
4444
<argument>false</argument>
4545

46-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
47-
<tag name="serializer.normalizer" priority="-922" />
46+
<!-- Run before serializer.normalizer.json_serializable -->
47+
<tag name="serializer.normalizer" priority="-890" />
4848
</service>
4949

5050
<service id="api_platform.jsonapi.normalizer.object" class="ApiPlatform\Core\JsonApi\Serializer\ObjectNormalizer" public="false">
@@ -53,8 +53,8 @@
5353
<argument type="service" id="api_platform.resource_class_resolver" />
5454
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
5555

56-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
57-
<tag name="serializer.normalizer" priority="-924" />
56+
<!-- Run after serializer.denormalizer.array but before serializer.normalizer.object -->
57+
<tag name="serializer.normalizer" priority="-995" />
5858
</service>
5959

6060
<service id="api_platform.jsonapi.normalizer.constraint_violation_list" class="ApiPlatform\Core\JsonApi\Serializer\ConstraintViolationListNormalizer" public="false">

src/Bridge/Symfony/Bundle/Resources/config/jsonld.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@
2929
<argument type="tagged" tag="api_platform.data_transformer" on-invalid="ignore" />
3030
<argument>false</argument>
3131

32-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
33-
<tag name="serializer.normalizer" priority="-922" />
32+
<!-- Run before serializer.normalizer.json_serializable -->
33+
<tag name="serializer.normalizer" priority="-890" />
3434
</service>
3535

3636
<service id="api_platform.jsonld.normalizer.object" class="ApiPlatform\Core\JsonLd\Serializer\ObjectNormalizer" public="false">
3737
<argument type="service" id="serializer.normalizer.object" />
3838
<argument type="service" id="api_platform.iri_converter" />
3939
<argument type="service" id="api_platform.jsonld.context_builder" />
4040

41-
<!-- Run after serializer.normalizer.data_uri but before serializer.normalizer.object -->
42-
<tag name="serializer.normalizer" priority="-924" />
41+
<!-- Run after serializer.denormalizer.array but before serializer.normalizer.object -->
42+
<tag name="serializer.normalizer" priority="-995" />
4343
</service>
4444

4545
<service id="api_platform.jsonld.encoder" class="ApiPlatform\Core\Serializer\JsonEncoder" public="false">
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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\Core\Tests\Fixtures\TestBundle\Document;
15+
16+
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Enum\ContentStatus;
18+
use Doctrine\Common\Collections\ArrayCollection;
19+
use Doctrine\Common\Collections\Collection;
20+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
21+
use Symfony\Component\Serializer\Annotation\Groups;
22+
23+
/**
24+
* @ApiResource(
25+
* normalizationContext={
26+
* "groups"={"get_content"},
27+
* },
28+
* )
29+
*
30+
* @ODM\Document
31+
*/
32+
class Content implements \JsonSerializable
33+
{
34+
/**
35+
* @var int|null
36+
*
37+
* @ODM\Id(strategy="INCREMENT", type="integer")
38+
*/
39+
private $id;
40+
41+
/**
42+
* @var string|null
43+
*
44+
* @ODM\Field(type="string")
45+
*/
46+
private $contentType;
47+
48+
/**
49+
* @var Collection<Field>
50+
*
51+
* @ODM\ReferenceMany(
52+
* targetDocument=Field::class,
53+
* mappedBy="content",
54+
* strategy="set",
55+
* cascade={"persist"},
56+
* )
57+
*/
58+
private $fields;
59+
60+
/**
61+
* @var string
62+
*
63+
* @ODM\Field(type="string")
64+
*/
65+
private $status;
66+
67+
public function __construct()
68+
{
69+
$this->fields = new ArrayCollection();
70+
$this->status = ContentStatus::DRAFT;
71+
}
72+
73+
/**
74+
* @Groups({"get_content"})
75+
*/
76+
public function getId(): ?int
77+
{
78+
return $this->id;
79+
}
80+
81+
/**
82+
* @Groups({"get_content"})
83+
*/
84+
public function getContentType(): ?string
85+
{
86+
return $this->contentType;
87+
}
88+
89+
public function setContentType(string $contentType): void
90+
{
91+
$this->contentType = $contentType;
92+
}
93+
94+
/**
95+
* @return array<string, Field>
96+
*/
97+
public function getFields(): array
98+
{
99+
return $this->fields->toArray();
100+
}
101+
102+
public function hasField(string $fieldName): bool
103+
{
104+
return isset($this->fields[$fieldName]);
105+
}
106+
107+
public function addField(Field $field): void
108+
{
109+
if ($this->hasField($field->getName())) {
110+
throw new \InvalidArgumentException(sprintf("Content already has '%s' field", $field->getName()));
111+
}
112+
113+
$this->fields[$field->getName()] = $field;
114+
$field->setContent($this);
115+
}
116+
117+
public function removeField(Field $field): void
118+
{
119+
unset($this->fields[$field->getName()]);
120+
121+
// set the owning side to null (unless already changed)
122+
if ($field->getContent() === $this) {
123+
$field->setContent(null);
124+
}
125+
}
126+
127+
/**
128+
* @Groups({"get_content"})
129+
*/
130+
public function getFieldValues(): array
131+
{
132+
$fieldValues = [];
133+
foreach ($this->getFields() as $field) {
134+
$fieldValues[$field->getName()] = $field->getValue();
135+
}
136+
137+
return $fieldValues;
138+
}
139+
140+
/**
141+
* @Groups({"get_content"})
142+
*/
143+
public function getStatus(): ContentStatus
144+
{
145+
return new ContentStatus($this->status);
146+
}
147+
148+
/**
149+
* {@inheritdoc}
150+
*/
151+
public function jsonSerialize()
152+
{
153+
return [
154+
'id' => $this->id,
155+
'contentType' => $this->contentType,
156+
'fields' => $this->fields,
157+
];
158+
}
159+
}

0 commit comments

Comments
 (0)