8000 bug #37628 [Serializer] Support multiple levels of discriminator mapp… · symfony/symfony@2dbbe50 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2dbbe50

Browse files
committed
bug #37628 [Serializer] Support multiple levels of discriminator mapping (jeroennoten)
This PR was merged into the 4.4 branch. Discussion ---------- [Serializer] Support multiple levels of discriminator mapping | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | License | MIT When using multiple levels of discriminator mapping for denormalizing to a multiple level class hierarchy, only the top level discriminator mapping is considered, resulting in an error: **Cannot instantiate abstract class ...** Example: ```php /** * @DiscriminatorMap(typeProperty="type", mapping={"group"=GroupNode::class, "item"=ItemNode::class}) */ abstract class Node { // ... } class GroupNode extends Node { // ... } /** * @DiscriminatorMap(typeProperty="item_type", mapping={"foo"=FooItemNode::class, "bar"=BarItemNode::class}) */ abstract class ItemNode extends Node { // ... } class FooItemNode { // ... } class BarItemNode { // ... } $objectNormalizer = new ObjectNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $node = $objectNormalizer->denormalize(['type' => 'item', 'item_type' => 'foo']); ``` This results in an error: **Cannot instantiate abstract class ItemNode**. Expected is that `$node` is of type `FooItemNode` after denormalization. The solution is to recursively call `AbstractObjectNormalizer::instantiateObject()` when a mapping is found, instead of always calling `parent::instantiateObject()`. Commits ------- 324ad95 [Serializer] Support multiple levels of discriminator mapping
2 parents 3619661 + 324ad95 commit 2dbbe50

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

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

Lines changed: 3 additions & 2 deletions
8000
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,9 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
230230
throw new RuntimeException(sprintf('The type "%s" has no mapped class for the abstract object "%s".', $type, $class));
231231
}
232232

233-
$class = $mappedClass;
234-
$reflectionClass = new \ReflectionClass($class);
233+
if ($mappedClass !== $class) {
234+
return $this->instantiateObject($data, $mappedClass, $context, new \ReflectionClass($mappedClass), $allowedAttributes, $format);
235+
}
235236
}
236237

237238
return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2020
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2121
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
22+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
2223
use Symfony\Component\Serializer\Mapping\ClassMetadata;
2324
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
2425
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
@@ -235,6 +236,43 @@ public function hasMetadataFor($value): bool
235236
$this->assertInstanceOf(DummySecondChildQuux::class, $normalizedData->quux);
236237
}
237238

239+
public function testDenormalizeWithNestedDiscriminatorMap()
240+
{
241+
$classDiscriminatorResolver = new class() implements ClassDiscriminatorResolverInterface {
242+
public function getMappingForClass(string $class): ?ClassDiscriminatorMapping
243+
{
244+
switch ($class) {
245+
case AbstractDummy::class:
246+
return new ClassDiscriminatorMapping('type', [
247+
'foo' => AbstractDummyFirstChild::class,
248+
]);
249+
case AbstractDummyFirstChild::class:
250+
return new ClassDiscriminatorMapping('nested_type', [
251+
'bar' => AbstractDummySecondChild::class,
252+
]);
253+
default:
254+
return null;
255+
}
256+
}
257+
258+
public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping
259+
{
260+
return null;
261+
}
262+
263+
public function getTypeForMappedObject($object): ?string
264+
{
265+
return null;
266+
}
267+
};
268+
269+
$normalizer = new AbstractObjectNormalizerDummy(null, null, null, $classDiscriminatorResolver);
270+
271+
$denormalizedData = $normalizer->denormalize(['type' => 'foo', 'nested_type' => 'bar'], AbstractDummy::class);
272+
273+
$this->assertInstanceOf(AbstractDummySecondChild::class, $denormalizedData);
274+
}
275+
238276
/**
239277
* Test that additional attributes throw an exception if no metadata factory is specified.
240278
*/

0 commit comments

Comments
 (0)
0