From 50d086ce1f045ca1472ed80e23389daca8b4ebd7 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 22 Nov 2023 06:10:11 +0100 Subject: [PATCH] [Serializer] Move discrimination to abstract --- .../Normalizer/AbstractObjectNormalizer.php | 18 +++++- .../Normalizer/GetSetMethodNormalizer.php | 4 ++ .../Normalizer/ObjectNormalizer.php | 16 ++--- .../Normalizer/PropertyNormalizer.php | 4 ++ .../Normalizer/GetSetMethodNormalizerTest.php | 63 +++++++++++++++++++ .../Normalizer/PropertyNormalizerTest.php | 43 +++++++++++++ 6 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 141ed4bb019ad..ea23899e71caa 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -179,8 +179,15 @@ public function normalize($object, string $format = null, array $context = []) $attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context); + $discriminatorProperty = null; + if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { + $discriminatorProperty = $mapping->getTypeProperty(); + } + try { - $attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext); + $attributeValue = $attribute === $discriminatorProperty + ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) + : $this->getAttributeValue($object, $attribute, $format, $attributeContext); } catch (UninitializedPropertyException $e) { if ($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? true) { continue; @@ -386,8 +393,15 @@ public function denormalize($data, string $type, string $format = null, array $c } if ($attributeContext[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) { + $discriminatorProperty = null; + if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { + $discriminatorProperty = $mapping->getTypeProperty(); + } + try { - $attributeContext[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $attributeContext); + $attributeContext[self::OBJECT_TO_POPULATE] = $attribute === $discriminatorProperty + ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) + : $this->getAttributeValue($object, $attribute, $format, $attributeContext); } catch (NoSuchPropertyException $e) { } } diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index d9339df64df5c..484a8fd4b7aae 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -67,6 +67,10 @@ public function hasCacheableSupportsMethod(): bool */ private function supports(string $class): bool { + if (null !== $this->classDiscriminatorResolver && $this->classDiscriminatorResolver->getMappingForClass($class)) { + return true; + } + $class = new \ReflectionClass($class); $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 1bce3ebeb1562..eb3d9716a13ef 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -30,8 +30,6 @@ class ObjectNormalizer extends AbstractObjectNormalizer { protected $propertyAccessor; - private $discriminatorCache = []; - private $objectClassResolver; 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 */ protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []) { - $cacheKey = \get_class($object); - if (!\array_key_exists($cacheKey, $this->discriminatorCache)) { - $this->discriminatorCache[$cacheKey] = null; - if (null !== $this->classDiscriminatorResolver) { - $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object); - $this->discriminatorCache[$cacheKey] = null === $mapping ? null : $mapping->getTypeProperty(); - } + $discriminatorProperty = null; + if (null !== $this->classDiscriminatorResolver && null !== $mapping = $this->classDiscriminatorResolver->getMappingForMappedObject($object)) { + $discriminatorProperty = $mapping->getTypeProperty(); } - return $attribute === $this->discriminatorCache[$cacheKey] ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) : $this->propertyAccessor->getValue($object, $attribute); + return $attribute === $discriminatorProperty + ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) + : $this->propertyAccessor->getValue($object, $attribute); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 38d81d9c9615e..03060344690b1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -61,6 +61,10 @@ public function hasCacheableSupportsMethod(): bool */ private function supports(string $class): bool { + if (null !== $this->classDiscriminatorResolver && $this->classDiscriminatorResolver->getMappingForClass($class)) { + return true; + } + $class = new \ReflectionClass($class); // We look for at least one non-static property diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index c2d670cfe5838..cdbb2d6fb0c79 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -16,7 +16,9 @@ use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; @@ -498,6 +500,27 @@ protected function getNormalizerForSkipUninitializedValues(): NormalizerInterfac { return new GetSetMethodNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); } + + public function testNormalizeWithDiscriminator() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); + $normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, null, $discriminator); + + $this->assertSame(['type' => 'one', 'url' => 'URL_ONE'], $normalizer->normalize(new GetSetMethodDiscriminatedDummyOne())); + } + + public function testDenormalizeWithDiscriminator() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); + $normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, null, $discriminator); + + $denormalized = new GetSetMethodDiscriminatedDummyTwo(); + $denormalized->setUrl('url'); + + $this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], GetSetMethodDummyInterface::class)); + } } class GetSetDummy @@ -762,3 +785,43 @@ public function __call($key, $value) throw new \RuntimeException('__call should not be called. Called with: '.$key); } } + +/** + * @DiscriminatorMap(typeProperty="type", mapping={ + * "one" = GetSetMethodDiscriminatedDummyOne::class, + * "two" = GetSetMethodDiscriminatedDummyTwo::class, + * }) + */ +interface GetSetMethodDummyInterface +{ +} + +class GetSetMethodDiscriminatedDummyOne implements GetSetMethodDummyInterface +{ + private string $url = 'URL_ONE'; + + public function getUrl(): string + { + return $this->url; + } + + public function setUrl(string $url): void + { + $this->url = $url; + } +} + +class GetSetMethodDiscriminatedDummyTwo implements GetSetMethodDummyInterface +{ + private string $url = 'URL_TWO'; + + public function getUrl(): string + { + return $this->url; + } + + public function setUrl(string $url): void + { + $this->url = $url; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index 2cf3a2ae0e6c8..f5b830c875fab 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -16,7 +16,9 @@ use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; +use Symfony\Component\Serializer\Annotation\DiscriminatorMap; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; @@ -457,6 +459,27 @@ protected function getNormalizerForSkipUninitializedValues(): NormalizerInterfac { return new PropertyNormalizer(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); } + + public function testNormalizeWithDiscriminator() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); + $normalizer = new PropertyNormalizer($classMetadataFactory, null, null, $discriminator); + + $this->assertSame(['type' => 'one', 'url' => 'URL_ONE'], $normalizer->normalize(new PropertyDiscriminatedDummyOne())); + } + + public function testDenormalizeWithDiscriminator() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); + $normalizer = new PropertyNormalizer($classMetadataFactory, null, null, $discriminator); + + $denormalized = new PropertyDiscriminatedDummyTwo(); + $denormalized->url = 'url'; + + $this->assertEquals($denormalized, $normalizer->denormalize(['type' => 'two', 'url' => 'url'], PropertyDummyInterface::class)); + } } class PropertyDummy @@ -560,3 +583,23 @@ public function getIntMatrix(): array return $this->intMatrix; } } + +/** + * @DiscriminatorMap(typeProperty="type", mapping={ + * "one" = PropertyDiscriminatedDummyOne::class, + * "two" = PropertyDiscriminatedDummyTwo::class, + * }) + */ +interface PropertyDummyInterface +{ +} + +class PropertyDiscriminatedDummyOne implements PropertyDummyInterface +{ + public string $url = 'URL_ONE'; +} + +class PropertyDiscriminatedDummyTwo implements PropertyDummyInterface +{ + public string $url = 'URL_TWO'; +} 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