Skip to content

Commit

Permalink
Add deep populate option #21669
Browse files Browse the repository at this point in the history
  • Loading branch information
jewome62 committed Apr 2, 2019
1 parent aa5b6f9 commit 585e86b
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
-----

* added the list of constraint violations' parameters in `ConstraintViolationListNormalizer`
* added a `deep_object_to_populate` context option to recursive denormalize on `object_to_populate` object.

4.2.0
-----
Expand Down
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Normalizer;

use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
Expand All @@ -38,6 +39,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
const SKIP_NULL_VALUES = 'skip_null_values';
const MAX_DEPTH_HANDLER = 'max_depth_handler';
const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';
const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';

private $propertyTypeExtractor;
private $typesCache = [];
Expand Down Expand Up @@ -274,6 +276,13 @@ public function denormalize($data, $class, $format = null, array $context = [])
continue;
}

if ($context[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {
try {
$context[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $context);
} catch (NoSuchPropertyException $e) {
}
}

$value = $this->validateAndDenormalize($class, $attribute, $value, $format, $context);
try {
$this->setAttributeValue($object, $attribute, $value, $format, $context);
Expand Down
Expand Up @@ -26,7 +26,7 @@ trait ObjectToPopulateTrait
*/
protected function extractObjectToPopulate($class, array $context, $key = null)
{
$key = $key ?: 'object_to_populate';
$key = $key ?: AbstractNormalizer::OBJECT_TO_POPULATE;

if (isset($context[$key]) && \is_object($context[$key]) && $context[$key] instanceof $class) {
return $context[$key];
Expand Down
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Fixtures;

/**
* @author Jérôme Desjardin <jewome62@gmail.com>
*/
class DeepObjectPopulateChildDummy
{
public $foo;

public $bar;
}
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Fixtures;

/**
* @author Jérôme Desjardin <jewome62@gmail.com>
*/
class DeepObjectPopulateParentDummy
{
/**
* @var DeepObjectPopulateChildDummy|null
*/
private $child;

public function setChild(?DeepObjectPopulateChildDummy $child)
{
$this->child = $child;
}

public function getChild(): ?DeepObjectPopulateChildDummy
{
return $this->child;
}
}
Expand Up @@ -23,6 +23,8 @@
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateChildDummy;
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy;

class AbstractObjectNormalizerTest extends TestCase
{
Expand Down Expand Up @@ -171,6 +173,48 @@ public function testSkipNullValues()
$result = $normalizer->normalize($dummy, null, [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
$this->assertSame(['bar' => 'present'], $result);
}

public function testDeepObjectToPopulate()
{
$child = new DeepObjectPopulateChildDummy();
$child->bar = 'bar-old';
$child->foo = 'foo-old';

$parent = new DeepObjectPopulateParentDummy();
$parent->setChild($child);

$context = [
AbstractObjectNormalizer::OBJECT_TO_POPULATE => $parent,
AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
];

$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor());

$newChild = new DeepObjectPopulateChildDummy();
$newChild->bar = 'bar-new';
$newChild->foo = 'foo-old';

$serializer = $this->getMockBuilder(__NAMESPACE__.'\ObjectSerializerDenormalizer')->getMock();
$serializer
->method('supportsDenormalization')
->with($this->arrayHasKey('bar'),
$this->equalTo(DeepObjectPopulateChildDummy::class),
$this->isNull(),
$this->contains($child))
->willReturn(true);
$serializer->method('denormalize')->willReturn($newChild);

$normalizer->setSerializer($serializer);
$normalizer->denormalize([
'child' => [
'bar' => 'bar-new',
],
], 'Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy', null, $context);

$this->assertSame('bar-new', $parent->getChild()->bar);
$this->assertSame('foo-old', $parent->getChild()->foo);
}
}

class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
Expand Down Expand Up @@ -348,3 +392,7 @@ public function setSerializer(SerializerInterface $serializer)
$this->serializer = $serializer;
}
}

abstract class ObjectSerializerDenormalizer implements SerializerInterface, DenormalizerInterface
{
}

0 comments on commit 585e86b

Please sign in to comment.