[Serializer] Add Support of recursive denormalization on object_to_populate

This commit is contained in:
jewome62 2019-03-18 22:44:06 +01:00 committed by Fabien Potencier
parent fad7c8c01f
commit 5b723865b1
6 changed files with 114 additions and 1 deletions

View File

@ -6,6 +6,7 @@ CHANGELOG
* added the list of constraint violations' parameters in `ConstraintViolationListNormalizer`
* added support for serializing `DateTimeZone` objects
* added a `deep_object_to_populate` context option to recursive denormalize on `object_to_populate` object.
4.2.0
-----

View File

@ -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;
@ -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 = [];
@ -274,6 +276,13 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
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);

View File

@ -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];

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -23,6 +23,8 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
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
{
@ -171,6 +173,48 @@ class AbstractObjectNormalizerTest extends TestCase
$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
@ -348,3 +392,7 @@ class ArrayDenormalizerDummy implements DenormalizerInterface, SerializerAwareIn
$this->serializer = $serializer;
}
}
abstract class ObjectSerializerDenormalizer implements SerializerInterface, DenormalizerInterface
{
}