Skip to content

Commit e8ee3a6

Browse files
committed
[Serializer] Move discrimination to abstract
1 parent 48be4b3 commit e8ee3a6

File tree

6 files changed

+141
-14
lines changed

6 files changed

+141
-14
lines changed

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

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
*/
3838
abstract class AbstractObjectNormalizer extends AbstractNormalizer
3939
{
40+
/** @var array<string, string|null> */
41+
private array $discriminatorCache = [];
42+
4043
/**
4144
* Set to true to respect the max depth metadata on fields.
4245
*/
@@ -180,7 +183,9 @@ public function normalize($object, string $format = null, array $context = [])
180183
$attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context);
181184

182185
try {
183-
$attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext);
186+
$attributeValue = $attribute === $this->getDiscriminatorProperty($object)
187+
? $this->classDiscriminatorResolver->getTypeForMappedObject($object)
188+
: $this->getAttributeValue($object, $attribute, $format, $attributeContext);
184189
} catch (UninitializedPropertyException $e) {
185190
if ($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? true) {
186191
continue;
@@ -387,7 +392,9 @@ public function denormalize($data, string $type, string $format = null, array $c
387392

388393
if ($attributeContext[self::DEEP_OBJECT_TO_POPULATE] ?? $this->defaultContext[self::DEEP_OBJECT_TO_POPULATE] ?? false) {
389394
try {
390-
$attributeContext[self::OBJECT_TO_POPULATE] = $this->getAttributeValue($object, $attribute, $format, $attributeContext);
395+
$attributeContext[self::OBJECT_TO_POPULATE] = $attribute === $this->getDiscriminatorProperty($object)
396+
? $this->classDiscriminatorResolver->getTypeForMappedObject($object)
397+
: $this->getAttributeValue($object, $attribute, $format, $attributeContext);
391398
} catch (NoSuchPropertyException $e) {
392399
}
393400
}
@@ -787,4 +794,19 @@ private function isUninitializedValueError(\Error $e): bool
787794
&& str_starts_with($e->getMessage(), 'Typed property')
788795
&& str_ends_with($e->getMessage(), 'must not be accessed before initialization');
789796
}
797+
798+
protected function getDiscriminatorProperty(object $object): ?string
799+
{
800+
if (null === $this->classDiscriminatorResolver) {
801+
return null;
802+
}
803+
804+
$cacheKey = \get_class($object);
805+
806+
if (!\array_key_exists($cacheKey, $this->discriminatorCache)) {
807+
$this->discriminatorCache[$cacheKey] = $this->classDiscriminatorResolver->getMappingForMappedObject($object)?->getTypeProperty();
808+
}
809+
810+
return $this->discriminatorCache[$cacheKey];
811+
}
790812
}

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: 3 additions & 12 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,9 @@ 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-
}
138-
}
139-
140-
return $attribute === $this->discriminatorCache[$cacheKey] ? $this->classDiscriminatorResolver->getTypeForMappedObject($object) : $this->propertyAccessor->getValue($object, $attribute);
129+
return $attribute === $this->getDiscriminatorProperty($object)
130+
? $this->getDiscriminatorType($object)
131+
: $this->propertyAccessor->getValue($object, $attribute);
141132
}
142133

143134
/**

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