Skip to content

Commit 8f88c9e

Browse files
authored
Merge pull request #2482 from bendavies/prioritize-object-to-populate
Fix instantiating object with discriminator mapping
2 parents 42079f3 + 5f5dfa2 commit 8f88c9e

File tree

7 files changed

+93
-26
lines changed

7 files changed

+93
-26
lines changed

features/main/crud_abstract.feature

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,20 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
8080
"""
8181

8282
Scenario: Update a concrete resource
83-
When I add "Content-Type" header equal to "application/ld+json"
84-
And I send a "PUT" request to "/concrete_dummies/1" with body:
83+
When I add "Content-Type" header equal to "application/ld+json"
84+
And I send a "PUT" request to "/concrete_dummies/1" with body:
8585
"""
8686
{
8787
"@id": "/concrete_dummies/1",
8888
"instance": "Become real",
8989
"name": "A nice dummy"
9090
}
9191
"""
92-
Then the response status code should be 200
93-
And the response should be in JSON
94-
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
95-
And the header "Content-Location" should be equal to "/concrete_dummies/1"
96-
And the JSON should be equal to:
92+
Then the response status code should be 200
93+
And the response should be in JSON
94+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
95+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
96+
And the JSON should be equal to:
9797
"""
9898
{
9999
"@context": "/contexts/ConcreteDummy",
@@ -105,7 +105,61 @@ Feature: Create-Retrieve-Update-Delete on abstract resource
105105
}
106106
"""
107107

108+
Scenario: Update a concrete resource using abstract resource uri
109+
When I add "Content-Type" header equal to "application/ld+json"
110+
And I send a "PUT" request to "/abstract_dummies/1" with body:
111+
"""
112+
{
113+
"@id": "/concrete_dummies/1",
114+
"instance": "Become surreal",
115+
"name": "A nicer dummy"
116+
}
117+
"""
118+
Then the response status code should be 200
119+
And the response should be in JSON
120+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
121+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
122+
And the JSON should be equal to:
123+
"""
124+
{
125+
"@context": "/contexts/ConcreteDummy",
126+
"@id": "/concrete_dummies/1",
127+
"@type": "ConcreteDummy",
128+
"instance": "Become surreal",
129+
"id": 1,
130+
"name": "A nicer dummy"
131+
}
132+
"""
133+
108134
Scenario: Delete a resource
109135
When I send a "DELETE" request to "/abstract_dummies/1"
110136
Then the response status code should be 204
111137
And the response should be empty
138+
139+
@createSchema
140+
Scenario: Create a concrete resource with discriminator
141+
When I add "Content-Type" header equal to "application/ld+json"
142+
And I send a "POST" request to "/abstract_dummies" with body:
143+
"""
144+
{
145+
"discr": "concrete",
146+
"instance": "Concrete",
147+
"name": "My Dummy"
148+
}
149+
"""
150+
Then the response status code should be 201
151+
And the response should be in JSON
152+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
153+
And the header "Content-Location" should be equal to "/concrete_dummies/1"
154+
And the header "Location" should be equal to "/concrete_dummies/1"
155+
And the JSON should be equal to:
156+
"""
157+
{
158+
"@context": "/contexts/ConcreteDummy",
159+
"@id": "/concrete_dummies/1",
160+
"@type": "ConcreteDummy",
161+
"instance": "Concrete",
162+
"id": 1,
163+
"name": "My Dummy"
164+
}
165+
"""

src/EventListener/WriteListener.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
6565

6666
$event->setControllerResult($persistResult ?? $controllerResult);
6767

68-
// Controller result must be immutable for _api_write_item_iri
69-
// if it's class changed compared to the base class let's avoid calling the IriConverter
70-
// especially that the Output class could be a DTO that's not referencing any route
7168
if (null === $this->iriConverter) {
7269
return;
7370
}
@@ -79,8 +76,7 @@ public function onKernelView(GetResponseForControllerResultEvent $event)
7976
$hasOutput = \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class'];
8077
}
8178

82-
$class = \get_class($controllerResult);
83-
if ($hasOutput && $attributes['resource_class'] === $class && $class === \get_class($event->getControllerResult())) {
79+
if ($hasOutput) {
8480
$request->attributes->set('_api_write_item_iri', $this->iriConverter->getIriFromItem($controllerResult));
8581
}
8682
break;

src/Serializer/AbstractItemNormalizer.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ public function denormalize($data, $class, $format = null, array $context = [])
185185
*/
186186
protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, string $format = null)
187187
{
188+
if (null !== $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) {
189+
unset($context[static::OBJECT_TO_POPULATE]);
190+
191+
return $object;
192+
}
193+
188194
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
189195
if (!isset($data[$mapping->getTypeProperty()])) {
190196
throw new RuntimeException(sprintf('Type property "%s" not found for the abstract object "%s"', $mapping->getTypeProperty(), $class));
@@ -199,12 +205,6 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
199205
$reflectionClass = new \ReflectionClass($class);
200206
}
201207

202-
if (null !== $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) {
203-
unset($context[static::OBJECT_TO_POPULATE]);
204-
205-
return $object;
206-
}
207-
208208
$constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes);
209209
if ($constructor) {
210210
$constructorParameters = $constructor->getParameters();

tests/EventListener/WriteListenerTest.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@
1818
use ApiPlatform\Core\EventListener\WriteListener;
1919
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
2020
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
21+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConcreteDummy;
2122
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
22-
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritance;
23-
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyTableInheritanceChild;
2423
use PHPUnit\Framework\TestCase;
2524
use Symfony\Component\HttpFoundation\Request;
2625
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -270,16 +269,16 @@ public function testOnKernelViewWithNoResourceClass()
270269

271270
public function testOnKernelViewWithParentResourceClass()
272271
{
273-
$dummy = new DummyTableInheritanceChild();
272+
$dummy = new ConcreteDummy();
274273

275274
$dataPersisterProphecy = $this->prophesize(DataPersisterInterface::class);
276275
$dataPersisterProphecy->supports($dummy)->willReturn(true)->shouldBeCalled();
277276
$dataPersisterProphecy->persist($dummy)->willReturn($dummy)->shouldBeCalled();
278277

279278
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
280-
$iriConverterProphecy->getIriFromItem($dummy)->shouldNotBeCalled();
279+
$iriConverterProphecy->getIriFromItem($dummy)->willReturn('/dummy/1')->shouldBeCalled();
281280

282-
$request = new Request([], [], ['_api_resource_class' => DummyTableInheritance::class, '_api_item_operation_name' => 'put', '_api_persist' => true]);
281+
$request = new Request([], [], ['_api_resource_class' => ConcreteDummy::class, '_api_item_operation_name' => 'put', '_api_persist' => true]);
283282
$request->setMethod('PUT');
284283

285284
$event = new GetResponseForControllerResultEvent(

tests/Fixtures/TestBundle/Document/AbstractDummy.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
* Abstract Dummy.
2323
*
2424
* @author Jérémy Derussé <[email protected]>
25-
*
26-
* @ApiResource(attributes={"filters"={"my_dummy.mongodb.search", "my_dummy.mongodb.order", "my_dummy.mongodb.date"}})
25+
* @ApiResource(
26+
* collectionOperations={"get", "post"},
27+
* itemOperations={"get", "put", "delete"},
28+
* attributes={"filters"={"my_dummy.mongodb.search", "my_dummy.mongodb.order", "my_dummy.mongodb.date"}}
29+
* )
2730
* @ODM\Document
2831
* @ODM\InheritanceType("SINGLE_COLLECTION")
2932
* @ODM\DiscriminatorField(value="discr")

tests/Fixtures/TestBundle/Entity/AbstractDummy.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
*
2424
* @author Jérémy Derussé <[email protected]>
2525
*
26-
* @ApiResource(attributes={"filters"={"my_dummy.search", "my_dummy.order", "my_dummy.date"}})
26+
* @ApiResource(
27+
* collectionOperations={"get", "post"},
28+
* itemOperations={"get", "put", "delete"},
29+
* attributes={"filters"={"my_dummy.search", "my_dummy.order", "my_dummy.date"}}
30+
* )
2731
* @ORM\Entity
2832
* @ORM\InheritanceType("SINGLE_TABLE")
2933
* @ORM\DiscriminatorColumn(name="discr", type="string", length=16)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\AbstractDummy:
2+
discriminator_map:
3+
type_property: discr
4+
mapping:
5+
concrete: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\ConcreteDummy'
6+
7+
ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\AbstractDummy:
8+
discriminator_map:
9+
type_property: discr
10+
mapping:
11+
concrete: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\ConcreteDummy'

0 commit comments

Comments
 (0)