Skip to content

Commit f8ccee0

Browse files
authored
Merge pull request #3479 from silverbackdan/patch/dto-output-class-same-as-original
Bug Fix: DataTransformer returning same class as original should include JSONLD Data
2 parents 3137ed0 + 8f9087f commit f8ccee0

File tree

12 files changed

+399
-8
lines changed

12 files changed

+399
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 2.5.x-dev
4+
5+
* JSON Schema: Missing LD context from Data Transformers #3479
6+
37
## 2.5.5
48

59
* Filter: Improve the RangeFilter query in case the values are equals using the between operator #3488

features/bootstrap/DoctrineContext.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoCustom as DummyDtoCustomDocument;
3636
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoNoInput as DummyDtoNoInputDocument;
3737
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoNoOutput as DummyDtoNoOutputDocument;
38+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoOutputFallbackToSameClass as DummyDtoOutputFallbackToSameClassDocument;
39+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoOutputSameClass as DummyDtoOutputSameClassDocument;
3840
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyFriend as DummyFriendDocument;
3941
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyGroup as DummyGroupDocument;
4042
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyOffer as DummyOfferDocument;
@@ -89,6 +91,8 @@
8991
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoCustom;
9092
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoNoInput;
9193
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoNoOutput;
94+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoOutputFallbackToSameClass;
95+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoOutputSameClass;
9296
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyFriend;
9397
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyGroup;
9498
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyImmutableDate;
@@ -1358,6 +1362,32 @@ public function thereAreNbDummyDtoCustom($nb)
13581362
$this->manager->clear();
13591363
}
13601364

1365+
/**
1366+
* @Given there is a DummyDtoOutputSameClass
1367+
*/
1368+
public function thereIsADummyDtoOutputSameClass()
1369+
{
1370+
$dto = $this->isOrm() ? new DummyDtoOutputSameClass() : new DummyDtoOutputSameClassDocument();
1371+
$dto->lorem = 'test';
1372+
$dto->ipsum = '1';
1373+
$this->manager->persist($dto);
1374+
$this->manager->flush();
1375+
$this->manager->clear();
1376+
}
1377+
1378+
/**
1379+
* @Given there is a DummyDtoOutputFallbackToSameClass
1380+
*/
1381+
public function thereIsADummyDtoOutputFallbackToSameClass()
1382+
{
1383+
$dto = $this->isOrm() ? new DummyDtoOutputFallbackToSameClass() : new DummyDtoOutputFallbackToSameClassDocument();
1384+
$dto->lorem = 'test';
1385+
$dto->ipsum = '1';
1386+
$this->manager->persist($dto);
1387+
$this->manager->flush();
1388+
$this->manager->clear();
1389+
}
1390+
13611391
/**
13621392
* @Given there is an order with same customer and recipient
13631393
*/

features/jsonld/input_output.feature

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,44 @@ Feature: JSON-LD DTO input and output
8383
}
8484
"""
8585

86+
@createSchema
87+
Scenario: Get an item with same class as custom output
88+
Given there is a DummyDtoOutputSameClass
89+
When I send a "GET" request to "/dummy_dto_output_same_classes/1"
90+
Then the response status code should be 200
91+
And the response should be in JSON
92+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
93+
And the JSON should be equal to:
94+
"""
95+
{
96+
"@context": "/contexts/DummyDtoOutputSameClass",
97+
"@id": "/dummy_dto_output_same_classes/1",
98+
"@type": "DummyDtoOutputSameClass",
99+
"lorem": "test",
100+
"ipsum": "modified",
101+
"id": 1
102+
}
103+
"""
104+
105+
@createSchema
106+
Scenario: Get an item with a data transformer that will return the original class as a fallback
107+
Given there is a DummyDtoOutputFallbackToSameClass
108+
When I send a "GET" request to "/dummy_dto_output_fallback_to_same_classes/1"
109+
Then the response status code should be 200
110+
And the response should be in JSON
111+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
112+
And the JSON should be equal to:
113+
"""
114+
{
115+
"@context": "/contexts/DummyDtoOutputFallbackToSameClass",
116+
"@id": "/dummy_dto_output_fallback_to_same_classes/1",
117+
"@type": "DummyDtoOutputFallbackToSameClass",
118+
"lorem": "test",
119+
"ipsum": "modified",
120+
"id": 1
121+
}
122+
"""
123+
86124
@createSchema
87125
Scenario: Create a DummyDtoCustom object without output
88126
When I send a "POST" request to "/dummy_dto_custom_post_without_output" with body:

src/JsonLd/Serializer/ItemNormalizer.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ public function supportsNormalization($data, $format = null): bool
6565
*/
6666
public function normalize($object, $format = null, array $context = [])
6767
{
68-
if (null !== $this->getOutputClass($this->getObjectClass($object), $context)) {
68+
$objectClass = $this->getObjectClass($object);
69+
$outputClass = $this->getOutputClass($objectClass, $context);
70+
if (null !== $outputClass && !isset($context[self::IS_TRANSFORMED_TO_SAME_CLASS])) {
6971
return parent::normalize($object, $format, $context);
7072
}
7173

src/Serializer/AbstractItemNormalizer.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ abstract class AbstractItemNormalizer extends AbstractObjectNormalizer
5151
use ContextTrait;
5252
use InputOutputMetadataTrait;
5353

54+
public const IS_TRANSFORMED_TO_SAME_CLASS = 'is_transformed_to_same_class';
55+
5456
protected $propertyNameCollectionFactory;
5557
protected $propertyMetadataFactory;
5658
protected $iriConverter;
@@ -112,18 +114,24 @@ public function hasCacheableSupportsMethod(): bool
112114
*/
113115
public function normalize($object, $format = null, array $context = [])
114116
{
115-
if ($object !== $transformed = $this->transformOutput($object, $context)) {
117+
if (!($isTransformed = isset($context[self::IS_TRANSFORMED_TO_SAME_CLASS])) && $outputClass = $this->getOutputClass($this->getObjectClass($object), $context)) {
116118
if (!$this->serializer instanceof NormalizerInterface) {
117119
throw new LogicException('Cannot normalize the output because the injected serializer is not a normalizer');
118120
}
119121

120-
$context['api_normalize'] = true;
121-
$context['api_resource'] = $object;
122-
unset($context['output']);
123-
unset($context['resource_class']);
122+
if ($object !== $transformed = $this->transformOutput($object, $context, $outputClass)) {
123+
$context['api_normalize'] = true;
124+
$context['api_resource'] = $object;
125+
unset($context['output'], $context['resource_class']);
126+
} else {
127+
$context[self::IS_TRANSFORMED_TO_SAME_CLASS] = true;
128+
}
124129

125130
return $this->serializer->normalize($transformed, $format, $context);
126131
}
132+
if ($isTransformed) {
133+
unset($context[self::IS_TRANSFORMED_TO_SAME_CLASS]);
134+
}
127135

128136
$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class'] ?? null);
129137
$context = $this->initContext($resourceClass, $context);
@@ -637,9 +645,12 @@ protected function getDataTransformer($data, string $to, array $context = []): ?
637645
* For a given resource, it returns an output representation if any
638646
* If not, the resource is returned.
639647
*/
640-
protected function transformOutput($object, array $context = [])
648+
protected function transformOutput($object, array $context = [], string $outputClass = null)
641649
{
642-
$outputClass = $this->getOutputClass($this->getObjectClass($object), $context);
650+
if (null === $outputClass) {
651+
$outputClass = $this->getOutputClass($this->getObjectClass($object), $context);
652+
}
653+
643654
if (null !== $outputClass && null !== $dataTransformer = $this->getDataTransformer($object, $outputClass, $context)) {
644655
return $dataTransformer->transform($object, $outputClass, $context);
645656
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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\DataTransformer;
15+
16+
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoOutputFallbackToSameClass as DummyDtoOutputFallbackToSameClassDocument;
18+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoOutputSameClass as DummyDtoOutputSameClassDocument;
19+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\OutputDtoDummy;
20+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoOutputFallbackToSameClass;
21+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoOutputSameClass;
22+
23+
/**
24+
* @author Daniel West <[email protected]>
25+
*/
26+
final class OutputDtoSameClassTransformer implements DataTransformerInterface
27+
{
28+
/**
29+
* {@inheritdoc}
30+
*/
31+
public function transform($object, string $to, array $context = [])
32+
{
33+
if (
34+
!$object instanceof DummyDtoOutputFallbackToSameClass &&
35+
!$object instanceof DummyDtoOutputFallbackToSameClassDocument &&
36+
!$object instanceof DummyDtoOutputSameClass &&
37+
!$object instanceof DummyDtoOutputSameClassDocument
38+
) {
39+
throw new \InvalidArgumentException();
40+
}
41+
$object->ipsum = 'modified';
42+
43+
return $object;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function supportsTransformation($data, string $to, array $context = []): bool
50+
{
51+
return (($data instanceof DummyDtoOutputFallbackToSameClass || $data instanceof DummyDtoOutputFallbackToSameClassDocument) && OutputDtoDummy::class === $to) ||
52+
(($data instanceof DummyDtoOutputSameClass || $data instanceof DummyDtoOutputSameClassDocument) && (DummyDtoOutputSameClass::class === $to || DummyDtoOutputSameClassDocument::class === $to));
53+
}
54+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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\Dto\OutputDtoDummy;
18+
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
19+
20+
/**
21+
* Dummy InputOutput.
22+
*
23+
* @author Daniel West <[email protected]>
24+
*
25+
* @ApiResource(attributes={"output"=OutputDtoDummy::class})
26+
* @ODM\Document
27+
*/
28+
class DummyDtoOutputFallbackToSameClass
29+
{
30+
/**
31+
* @var int The id
32+
*
33+
* @ODM\Id(strategy="INCREMENT", type="integer", nullable=true)
34+
*/
35+
private $id;
36+
37+
/**
38+
* @var string
39+
*
40+
* @ODM\Field
41+
*/
42+
public $lorem;
43+
44+
/**
45+
* @var string
46+
*
47+
* @ODM\Field
48+
*/
49+
public $ipsum;
50+
51+
public function getId()
52+
{
53+
return $this->id;
54+
}
55+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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 Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
18+
19+
/**
20+
* Dummy InputOutput.
21+
*
22+
* @author Daniel West <[email protected]>
23+
*
24+
* @ApiResource(attributes={"output"=DummyDtoOutputSameClass::class})
25+
* @ODM\Document
26+
*/
27+
class DummyDtoOutputSameClass
28+
{
29+
/**
30+
* @var int The id
31+
*
32+
* @ODM\Id(strategy="INCREMENT", type="integer", nullable=true)
33+
*/
34+
private $id;
35+
36+
/**
37+
* @var string
38+
*
39+
* @ODM\Field
40+
*/
41+
public $lorem;
42+
43+
/**
44+
* @var string
45+
*
46+
* @ODM\Field
47+
*/
48+
public $ipsum;
49+
50+
public function getId()
51+
{
52+
return $this->id;
53+
}
54+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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\Dto;
15+
16+
/**
17+
* OutputDtoDummy.
18+
*
19+
* @author Daniel West <[email protected]>
20+
*/
21+
class OutputDtoDummy
22+
{
23+
public $foo = 'foo';
24+
}

0 commit comments

Comments
 (0)