Skip to content

Commit b8e9ec3

Browse files
bug #52681 [Serializer] Fix support for DiscriminatorMap in PropertyNormalizer (mtarld)
This PR was merged into the 5.4 branch. Discussion ---------- [Serializer] Fix support for DiscriminatorMap in PropertyNormalizer | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #36585 | License | MIT Follow up of #36695 Commits ------- 50d086c [Serializer] Move discrimination to abstract
2 parents 1da3a5c + 50d086c commit b8e9ec3

File tree

6 files changed

+136
-12
lines changed

6 files changed

+136
-12
lines changed

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,15 @@ public function normalize($object, string $format = null, array $context = [])
179179

180180
$attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context);
181181

182+
$discriminatorProperty = null;
183+
if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
184+
$discriminatorProperty = $mapping->getTypeProperty();
185+
}
186+
182187
try {
183-
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext);
188+
$attributeValue = $attribute === $discriminatorProperty
189+
? $this->classDiscriminatorResolver->getTypeForMappedObject($object)
190+
: $this->getAttributeValue($object, $attribute, $format, $attributeContext);
184191
} catch (UninitializedPropertyException $e) {
185192
if ($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? true) {
186193
continue;
@@ -382,8 +389,15 @@ public function denormalize($data, string $type, string $format = null, array $c
382389
}
383390

384391
if ($attributeContext[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {
392+
$discriminatorProperty = null;
393+
if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
394+
$discriminatorProperty = $mapping->getTypeProperty();
395+
}
396+
385397
try {
386-
$attributeContext[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $attributeContext);
398+
$attributeContext[self::OBJECT_TO_POPULATE] = $attribute === $discriminatorProperty
399+
? $this->classDiscriminatorResolver->getTypeForMappedObject($object)
400+
: $this->getAttributeValue($object, $attribute, $format, $attributeContext);
387401
} catch (NoSuchPropertyException $e) {
388402
}
389403
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public function hasCacheableSupportsMethod(): bool
6767
*/
6868
private function supports(string $class): bool
6969
{
70+
if (null !== $this->classDiscriminatorResolver && $this->classDiscriminatorResolver->getMappingForClass($class)) {
71+
return true;
72+
}
73+
7074
$class = new \ReflectionClass($class);
7175
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
7276
foreach ($methods as $method) {

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class ObjectNormalizer extends AbstractObjectNormalizer
3030
{
3131
protected $propertyAccessor;
3232

33-
private $discriminatorCache = [];
34-
3533
private $objectClassResolver;
3634

3735
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
@@ -128,16 +126,14 @@ protected function extractAttributes(object $object, string $format = null, arra
128126
*/
129127
protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = [])
130128
{
131-
$cacheKey = \get_class($object);
132-
if (!\array_key_exists($cacheKey, $this->discriminatorCache)) {
133-
$this->discriminatorCache[$cacheKey] = null;
134-
if (null !== $this->classDiscriminatorResolver) {
135-
$mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object);
136-
$this->discriminatorCache[$cacheKey] = null === $mapping ? null : $mapping->getTypeProperty();
137-
}
129+
$discriminatorProperty = null;
130+
if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) {
131+
$discriminatorProperty = $mapping->getTypeProperty();
138132
}
139133

140-
return $attribute === $this->discriminatorCache[$cacheKey] ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) : $this->propertyAccessor->getValue($object, $attribute);
134+
return $attribute === $discriminatorProperty
135+
? $this->classDiscriminatorResolver->getTypeForMappedObject($object)
136+
: $this->propertyAccessor->getValue($object, $attribute);
141137
}
142138

143139
/**

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public function hasCacheableSupportsMethod(): bool
6161
*/
6262
private function supports(string $class): bool
6363
{
64+
if (null !== $this->classDiscriminatorResolver && $this->classDiscriminatorResolver->getMappingForClass($class)) {
65+
return true;
66+
}
67+
6468
$class = new \ReflectionClass($class);
6569

6670
// We look for at least one non-static property

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1717
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
1818
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
19+
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
1920
use Symfony\Component\Serializer\Exception\LogicException;
21+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2022
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
2123
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
2224
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
@@ -498,6 +500,27 @@ protected function getNormalizerForSkipUninitializedValues(): NormalizerInterfac
498500
{
499501
return new GetSetMethodNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())));
500502
}
503+
504+
public function testNormalizeWithDiscriminator()
505+
{
506+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
507+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
508+
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, null, $discriminator);
509+
510+
$this->assertSame(['type' => 'one', 'url' => 'URL_ONE'], $normalizer->normalize(new GetSetMethodDiscriminatedDummyOne()));
511+
}
512+
513+
public function testDenormalizeWithDiscriminator()
514+
{
515+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
516+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
517+
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, null, $discriminator);
518+
519+
$denormalized = new GetSetMethodDiscriminatedDummyTwo();
520+
$denormalized->setUrl('url');
521+
522+
$this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], GetSetMethodDummyInterface::class));
523+
}
501524
}
502525

503526
class GetSetDummy
@@ -762,3 +785,43 @@ public function __call($key, $value)
762785
throw new \RuntimeException('__call should not be called. Called with: '.$key);
763786
}
764787
}
788+
789+
/**
790+
* @DiscriminatorMap(typeProperty="type", mapping={
791+
* "one" = GetSetMethodDiscriminatedDummyOne::class,
792+
* "two" = GetSetMethodDiscriminatedDummyTwo::class,
793+
* })
794+
*/
795+
interface GetSetMethodDummyInterface
796+
{
797+
}
798+
799+
class GetSetMethodDiscriminatedDummyOne implements GetSetMethodDummyInterface
800+
{
801+
private string $url = 'URL_ONE';
802+
803+
public function getUrl(): string
804+
{
805+
return $this->url;
806+
}
807+
808+
public function setUrl(string $url): void
809+
{
810+
$this->url = $url;
811+
}
812+
}
813+
814+
class GetSetMethodDiscriminatedDummyTwo implements GetSetMethodDummyInterface
815+
{
816+
private string $url = 'URL_TWO';
817+
818+
public function getUrl(): string
819+
{
820+
return $this->url;
821+
}
822+
823+
public function setUrl(string $url): void
824+
{
825+
$this->url = $url;
826+
}
827+
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1717
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
1818
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
19+
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
1920
use Symfony\Component\Serializer\Exception\LogicException;
21+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2022
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
2123
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
2224
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
@@ -457,6 +459,27 @@ protected function getNormalizerForSkipUninitializedValues(): NormalizerInterfac
457459
{
458460
return new PropertyNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())));
459461
}
462+
463+
public function testNormalizeWithDiscriminator()
464+
{
465+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
466+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
467+
$normalizer = new PropertyNormalizer($classMetadataFactory, null, null, $discriminator);
468+
469+
$this->assertSame(['type' => 'one', 'url' => 'URL_ONE'], $normalizer->normalize(new PropertyDiscriminatedDummyOne()));
470+
}
471+
472+
public function testDenormalizeWithDiscriminator()
473+
{
474+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
475+
$discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory);
476+
$normalizer = new PropertyNormalizer($classMetadataFactory, null, null, $discriminator);
477+
478+
$denormalized = new PropertyDiscriminatedDummyTwo();
479+
$denormalized->url = 'url';
480+
481+
$this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], PropertyDummyInterface::class));
482+
}
460483
}
461484

462485
class PropertyDummy
@@ -560,3 +583,23 @@ public function getIntMatrix(): array
560583
return $this->intMatrix;
561584
}
562585
}
586+
587+
/**
588+
* @DiscriminatorMap(typeProperty="type", mapping={
589+
* "one" = PropertyDiscriminatedDummyOne::class,
590+
* "two" = PropertyDiscriminatedDummyTwo::class,
591+
* })
592+
*/
593+
interface PropertyDummyInterface
594+
{
595+
}
596+
597+
class PropertyDiscriminatedDummyOne implements PropertyDummyInterface
598+
{
599+
public string $url = 'URL_ONE';
600+
}
601+
602+
class PropertyDiscriminatedDummyTwo implements PropertyDummyInterface
603+
{
604+
public string $url = 'URL_TWO';
605+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy