8000 bug #51369 [Serializer] Fix deserializing object collection propertie… · symfony/symfony@469f1cc · GitHub
[go: up one dir, main page]

Skip to content

Commit 469f1cc

Browse files
bug #51369 [Serializer] Fix deserializing object collection properties (X-Coder264)
This PR was merged into the 6.3 branch. Discussion ---------- [Serializer] Fix deserializing object collection properties | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #51261 | License | MIT | Doc PR | - On Symfony <= 6.2 serializer versions the `supportsDenormalization` was always called in `\Symfony\Component\Serializer\Serializer::getDenormalizer`. When the `getSupportedTypes` logic was introduced for Symfony 6.3 serializer some `if` statement conditions were introduced and if they are `true` the `supportsDenormalization` logic is not called at all anymore. Those conditions currently prevent a legit use-case that worked prior to 6.3 from calling the `supportsDenormalization` method - the case being when the denormalizer is supposed to denormalize a collection/array of objects. This use-case still works even on the 6.3 serializer as long as the denormalizer does not implement the new `getSupportedTypes` method, but that is triggering a deprecation. This PR aims to fix that so that denormalizing an array of objects still works even when using the new `getSupportedTypes` method in a denormalizer. cc `@tucksaun` `@nicolas`-grekas Commits ------- 32dc134 [Serializer] Fix deserializing object collection properties
2 parents bf4ea59 + 32dc134 commit 469f1cc

File tree

6 files changed

+132
-0
lines changed

6 files changed

+132
-0
lines changed

src/Symfony/Component/Serializer/Serializer.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,12 @@ private function getDenormalizer(mixed $data, string $class, ?string $format, ar
359359

360360
$supportedTypes = $normalizer->getSupportedTypes($format);
361361

362+
$doesClassRepresentCollection = str_ends_with($class, '[]');
363+
362364
foreach ($supportedTypes as $supportedType => $isCacheable) {
363365
if (\in_array($supportedType, ['*', 'object'], true)
364366
|| $class !== $supportedType && ('object' !== $genericType || !is_subclass_of($class, $supportedType))
367+
&& !($doesClassRepresentCollection && str_ends_with($supportedType, '[]') && is_subclass_of(strstr($class, '[]', true), strstr($supportedType, '[]', true)))
365368
) {
366369
continue;
367370
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
interface FooDummyInterface
15+
{
16+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
8000 2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
final class FooImplementationDummy implements FooDummyInterface
15+
{
16+
/**
17+
* @var string
18+
*/
19+
public $name;
20+
}
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 Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
15+
16+
final class FooInterfaceDummyDenormalizer implements DenormalizerInterface
17+
{
18+
public function denormalize(mixed $data, string $type, string $format = null, array $context = []): array
19+
{
20+
$result = [];
21+
foreach ($data as $foo) {
22+
$fooDummy = new FooImplementationDummy();
23+
$fooDummy->name = $foo['name'];
24+
$result[] = $fooDummy;
25+
}
26+
27+
return $result;
28+
}
29+
30+
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
31+
{
32+
if (str_ends_with($type, '[]')) {
33+
$className = substr($type, 0, -2);
34+
$classImplements = class_implements($className);
35+
\assert(\is_array($classImplements));
36+
37+
return class_exists($className) && \in_array(FooDummyInterface::class, $classImplements, true);
38+
}
39+
40+
return false;
41+
}
42+
43+
/**
44+
* @return array<string, bool>
45+
*/
46+
public function getSupportedTypes(?string $format): array
47+
{
48+
return [FooDummyInterface::class.'[]' => false];
49+
}
50+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.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+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
final class ObjectCollectionPropertyDummy
15+
{
16+
/**
17+
* @var FooImplementationDummy[]
18+
*/
19+
public $foo;
20+
21+
public function getFoo(): array
22+
{
23+
return $this->foo;
24+
}
25+
}

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858
use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumConstructor;
5959
use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumProperty;
6060
use Symfony\Component\Serializer\Tests\Fixtures\FalseBuiltInDummy;
61+
use Symfony\Component\Serializer\Tests\Fixtures\FooImplementationDummy;
62+
use Symfony\Component\Serializer\Tests\Fixtures\FooInterfaceDummyDenormalizer;
6163
use Symfony\Component\Serializer\Tests\Fixtures\NormalizableTraversableDummy;
64+
use Symfony\Component\Serializer\Tests\Fixtures\ObjectCollectionPropertyDummy;
6265
use Symfony\Component\Serializer\Tests\Fixtures\Php74Full;
6366
use Symfony\Component\Serializer\Tests\Fixtures\Php80WithPromotedTypedConstructor;
6467
use Symfony\Component\Serializer\Tests\Fixtures\TraversableDummy;
@@ -711,6 +714,21 @@ public function testDeserializeInconsistentScalarArray()
711714
$serializer->deserialize('["42"]', 'int[]', 'json');
712715
}
713716

717+
public function testDeserializeOnObjectWithObjectCollectionProperty()
718+
{
719+
$serializer = new Serializer([new FooInterfaceDummyDenormalizer(), new ObjectNormalizer(null, null, null, new PhpDocExtractor())], [new JsonEncoder()]);
720+
721+
$obj = $serializer->deserialize('{"foo":[{"name":"bar"}]}', ObjectCollectionPropertyDummy::class, 'json');
722+
$this->assertInstanceOf(ObjectCollectionPropertyDummy::class, $obj);
723+
724+
$fooDummyObjects = $obj->getFoo();
725+
$this->assertCount(1, $fooDummyObjects);
726+
727+
$fooDummyObject = $fooDummyObjects[0];
728+
$this->assertInstanceOf(FooImplementationDummy::class, $fooDummyObject);
729+
$this->assertSame('bar', $fooDummyObject->name);
730+
}
731+
714732
public function testDeserializeWrappedScalar()
715733
{
716734
$serializer = new Serializer([new UnwrappingDenormalizer()], ['json' => new JsonEncoder()]);

0 commit comments

Comments
 (0)
0