Skip to content

Commit 0d365a8

Browse files
committed
feature #30607 [Serializer] Add Support of recursive denormalization on object_to_populate (jewome62)
This PR was squashed before being merged into the 4.3-dev branch (closes #30607). Discussion ---------- [Serializer] Add Support of recursive denormalization on object_to_populate | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | Pending | Fixed tickets | #21669 | License | MIT | Doc PR | Pending Currently the deserialization re-create new sub-object with object_to_populate. This option permit to make object_to_populate recursive. Commits ------- 5b72386 [Serializer] Add Support of recursive denormalization on object_to_populate
2 parents 9e2f506 + 5b72386 commit 0d365a8

File tree

6 files changed

+114
-1
lines changed

6 files changed

+114
-1
lines changed

src/Symfony/Component/Serializer/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* added the list of constraint violations' parameters in `ConstraintViolationListNormalizer`
88
* added support for serializing `DateTimeZone` objects
9+
* added a `deep_object_to_populate` context option to recursive denormalize on `object_to_populate` object.
910

1011
4.2.0
1112
-----

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Serializer\Normalizer;
1313

1414
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
15+
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
1516
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
1617
use Symfony\Component\PropertyInfo\Type;
1718
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -38,6 +39,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
3839
const SKIP_NULL_VALUES = 'skip_null_values';
3940
const MAX_DEPTH_HANDLER = 'max_depth_handler';
4041
const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';
42+
const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';
4143

4244
private $propertyTypeExtractor;
4345
private $typesCache = [];
@@ -274,6 +276,13 @@ public function denormalize($data, $class, $format = null, array $context = [])
274276
continue;
275277
}
276278

279+
if ($context[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {
280+
try {
281+
$context[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $context);
282+
} catch (NoSuchPropertyException $e) {
283+
}
284+
}
285+
277286
$value = $this->validateAndDenormalize($class, $attribute, $value, $format, $context);
278287
try {
279288
$this->setAttributeValue($object, $attribute, $value, $format, $context);

src/Symfony/Component/Serializer/Normalizer/ObjectToPopulateTrait.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ trait ObjectToPopulateTrait
2626
*/
2727
protected function extractObjectToPopulate($class, array $context, $key = null)
2828
{
29-
$key = $key ?: 'object_to_populate';
29+
$key = $key ?? AbstractNormalizer::OBJECT_TO_POPULATE;
3030

3131
if (isset($context[$key]) && \is_object($context[$key]) && $context[$key] instanceof $class) {
3232
return $context[$key];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
/**
15+
* @author Jérôme Desjardin <jewome62@gmail.com>
16+
*/
17+
class DeepObjectPopulateChildDummy
18+
{
19+
public $foo;
20+
21+
public $bar;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
/**
15+
* @author Jérôme Desjardin <jewome62@gmail.com>
16+
*/
17+
class DeepObjectPopulateParentDummy
18+
{
19+
/**
20+
* @var DeepObjectPopulateChildDummy|null
21+
*/
22+
private $child;
23+
24+
public function setChild(?DeepObjectPopulateChildDummy $child)
25+
{
26+
$this->child = $child;
27+
}
28+
29+
public function getChild(): ?DeepObjectPopulateChildDummy
30+
{
31+
return $this->child;
32+
}
33+
}

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

+48
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2424
use Symfony\Component\Serializer\SerializerAwareInterface;
2525
use Symfony\Component\Serializer\SerializerInterface;
26+
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateChildDummy;
27+
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy;
2628

2729
class AbstractObjectNormalizerTest extends TestCase
2830
{
@@ -171,6 +173,48 @@ public function testSkipNullValues()
171173
$result = $normalizer->normalize($dummy, null, [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
172174
$this->assertSame(['bar' => 'present'], $result);
173175
}
176+
177+
public function testDeepObjectToPopulate()
178+
{
179+
$child = new DeepObjectPopulateChildDummy();
180+
$child->bar = 'bar-old';
181+
$child->foo = 'foo-old';
182+
183+
$parent = new DeepObjectPopulateParentDummy();
184+
$parent->setChild($child);
185+
186+
$context = [
187+
AbstractObjectNormalizer::OBJECT_TO_POPULATE => $parent,
188+
AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
189+
];
190+
191+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
192+
$normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor());
193+
194+
$newChild = new DeepObjectPopulateChildDummy();
195+
$newChild->bar = 'bar-new';
196+
$newChild->foo = 'foo-old';
197+
198+
$serializer = $this->getMockBuilder(__NAMESPACE__.'\ObjectSerializerDenormalizer')->getMock();
199+
$serializer
200+
->method('supportsDenormalization')
201+
->with($this->arrayHasKey('bar'),
202+
$this->equalTo(DeepObjectPopulateChildDummy::class),
203+
$this->isNull(),
204+
$this->contains($child))
205+
->willReturn(true);
206+
$serializer->method('denormalize')->willReturn($newChild);
207+
208+
$normalizer->setSerializer($serializer);
209+
$normalizer->denormalize([
210+
'child' => [
211+
'bar' => 'bar-new',
212+
],
213+
], 'Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy', null, $context);
214+
215+
$this->assertSame('bar-new', $parent->getChild()->bar);
216+
$this->assertSame('foo-old', $parent->getChild()->foo);
217+
}
174218
}
175219

176220
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
@@ -348,3 +392,7 @@ public function setSerializer(SerializerInterface $serializer)
348392
$this->serializer = $serializer;
349393
}
350394
}
395+
396+
abstract class ObjectSerializerDenormalizer implements SerializerInterface, DenormalizerInterface
397+
{
398+
}

0 commit comments

Comments
 (0)