Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4c70e94

Browse filesBrowse files
committed
Add class discriminator mapping to resolve abstract classes
Add the `discriminator_class_mapping` in the framework extension Discriminator map is compatible with interfaces
1 parent a603ba0 commit 4c70e94
Copy full SHA for 4c70e94

26 files changed

+847
-9
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@
2424

2525
<service id="serializer.property_accessor" alias="property_accessor" />
2626

27+
<!-- Discriminator Map -->
28+
<service id="serializer.mapping.class_discriminator_resolver" class="Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata">
29+
<argument type="service" id="serializer.mapping.class_metadata_factory" />
30+
</service>
31+
<service id="Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface" alias="serializer.mapping.class_discriminator_resolver" />
32+
2733
<!-- Normalizer -->
2834
<service id="serializer.normalizer.dateinterval" class="Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer">
2935
<!-- Run before serializer.normalizer.object -->
@@ -50,6 +56,7 @@
5056
<argument>null</argument> <!-- name converter -->
5157
<argument type="service" id="serializer.property_accessor" />
5258
<argument type="service" id="property_info" on-invalid="ignore" />
59+
<argument type="service" id="serializer.mapping.class_discriminator_resolver" />
5360

5461
<!-- Run after all custom normalizers -->
5562
<tag name="serializer.normalizer" priority="-1000" />
+64Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Annotation;
13+
14+
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
15+
16+
/**
17+
* Annotation class for @DiscriminatorMap().
18+
*
19+
* @Annotation
20+
* @Target({"CLASS"})
21+
*
22+
* @author Samuel Roze <samuel.roze@gmail.com>
23+
*/
24+
class DiscriminatorMap
25+
{
26+
/**
27+
* @var string
28+
*/
29+
private $typeProperty;
30+
31+
/**
32+
* @var array
33+
*/
34+
private $mapping;
35+
36+
/**
37+
* @param array $data
38+
*
39+
* @throws InvalidArgumentException
40+
*/
41+
public function __construct(array $data)
42+
{
43+
if (empty($data['typeProperty'])) {
44+
throw new InvalidArgumentException(sprintf('Parameter "typeProperty" of annotation "%s" cannot be empty.', get_class($this)));
45+
}
46+
47+
if (empty($data['mapping'])) {
48+
throw new InvalidArgumentException(sprintf('Parameter "mapping" of annotation "%s" cannot be empty.', get_class($this)));
49+
}
50+
51+
$this->typeProperty = $data['typeProperty'];
52+
$this->mapping = $data['mapping'];
53+
}
54+
55+
public function getTypeProperty(): string
56+
{
57+
return $this->typeProperty;
58+
}
59+
60+
public function getMapping(): array
61+
{
62+
return $this->mapping;
63+
}
64+
}
+92Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping;
13+
14+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
15+
16+
/**
17+
* @author Samuel Roze <samuel.roze@gmail.com>
18+
*/
19+
class ClassDiscriminatorFromClassMetadata implements ClassDiscriminatorResolverInterface
20+
{
21+
/**
22+
* @var ClassMetadataFactoryInterface
23+
*/
24+
private $classMetadataFactory;
25+
private $mappingForMappedObjectCache = array();
26+
27+
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
28+
{
29+
$this->classMetadataFactory = $classMetadataFactory;
30+
}
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function getMappingForClass(string $class): ?ClassDiscriminatorMapping
36+
{
37+
if ($this->classMetadataFactory->hasMetadataFor($class)) {
38+
return $this->classMetadataFactory->getMetadataFor($class)->getClassDiscriminatorMapping();
39+
}
40+
41+
return null;
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping
48+
{
49+
if ($this->classMetadataFactory->hasMetadataFor($object)) {
50+
$metadata = $this->classMetadataFactory->getMetadataFor($object);
51+
52+
if (null !== $metadata->getClassDiscriminatorMapping()) {
53+
return $metadata->getClassDiscriminatorMapping();
54+
}
55+
}
56+
57+
$cacheKey = is_object($object) ? get_class($object) : $object;
58+
if (!array_key_exists($cacheKey, $this->mappingForMappedObjectCache)) {
59+
$this->mappingForMappedObjectCache[$cacheKey] = $this->resolveMappingForMappedObject($object);
60+
}
61+
62+
return $this->mappingForMappedObjectCache[$cacheKey];
63+
}
64+
65+
/**
66+
* {@inheritdoc}
67+
*/
68+
public function getTypeForMappedObject($object): ?string
69+
{
70+
if (null === $mapping = $this->getMappingForMappedObject($object)) {
71+
return null;
72+
}
73+
74+
return $mapping->getMappedObjectType($object);
75+
}
76+
77+
private function resolveMappingForMappedObject($object)
78+
{
79+
$reflectionClass = new \ReflectionClass($object);
80+
if ($parentClass = $reflectionClass->getParentClass()) {
81+
return $this->getMappingForMappedObject($parentClass->getName());
82+
}
83+
84+
foreach ($reflectionClass->getInterfaceNames() as $interfaceName) {
85+
if (null !== ($interfaceMapping = $this->getMappingForMappedObject($interfaceName))) {
86+
return $interfaceMapping;
87+
}
88+
}
89+
90+
return null;
91+
}
92+
}
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping;
13+
14+
/**
15+
* @author Samuel Roze <samuel.roze@gmail.com>
16+
*/
17+
class ClassDiscriminatorMapping
18+
{
19+
private $typeProperty;
20+
private $typesMapping;
21+
22+
public function __construct(string $typeProperty, array $typesMapping = array())
23+
{
24+
$this->typeProperty = $typeProperty;
25+
$this->typesMapping = $typesMapping;
26+
}
27+
28+
public function getTypeProperty(): string
29+
{
30+
return $this->typeProperty;
31+
}
32+
33+
public function getClassForType(string $type): ?string
34+
{
35+
if (isset($this->typesMapping[$type])) {
36+
return $this->typesMapping[$type];
37+
}
38+
39+
return null;
40+
}
41+
42+
/**
43+
* @param object|string $object
44+
*
45+
* @return string|null
46+
*/
47+
public function getMappedObjectType($object): ?string
48+
{
49+
foreach ($this->typesMapping as $type => $typeClass) {
50+
if (is_a($object, $typeClass)) {
51+
return $type;
52+
}
53+
}
54+
55+
return null;
56+
}
57+
58+
public function getTypesMapping(): array
59+
{
60+
return $this->typesMapping;
61+
}
62+
}
+41Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Mapping;
13+
14+
/**
15+
* Knows how to get the class discriminator mapping for classes and objects.
16+
*
17+
* @author Samuel Roze <samuel.roze@gmail.com>
18+
*/
19+
interface ClassDiscriminatorResolverInterface
20+
{
21+
/**
22+
* @param string $class
23+
*
24+
* @return ClassDiscriminatorMapping|null
25+
*/
26+
public function getMappingForClass(string $class): ?ClassDiscriminatorMapping;
27+
28+
/**
29+
* @param object|string $object
30+
*
31+
* @return ClassDiscriminatorMapping|null
32+
*/
33+
public function getMappingForMappedObject($object): ?ClassDiscriminatorMapping;
34+
35+
/**
36+
* @param object|string $object
37+
*
38+
* @return string|null
39+
*/
40+
public function getTypeForMappedObject($object): ?string;
41+
}

‎src/Symfony/Component/Serializer/Mapping/ClassMetadata.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/ClassMetadata.php
+34-1Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,25 @@ class ClassMetadata implements ClassMetadataInterface
3939
*/
4040
private $reflClass;
4141

42-
public function __construct(string $class)
42+
/**
43+
* @var ClassDiscriminatorMapping|null
44+
*
45+
* @internal This property is public in order to reduce the size of the
46+
* class' serialized representation. Do not access it. Use
47+
* {@link getClassDiscriminatorMapping()} instead.
48+
*/
49+
public $classDiscriminatorMapping;
50+
51+
/**
52+
* Constructs a metadata for the given class.
53+
*
54+
* @param string $class
55+
* @param ClassDiscriminatorMapping|null $classDiscriminatorMapping
56+
*/
57+
public function __construct(string $class, ClassDiscriminatorMapping $classDiscriminatorMapping = null)
4358
{
4459
$this->name = $class;
60+
$this->classDiscriminatorMapping = $classDiscriminatorMapping;
4561
}
4662

4763
/**
@@ -94,6 +110,22 @@ public function getReflectionClass()
94110
return $this->reflClass;
95111
}
96112

113+
/**
114+
* {@inheritdoc}
115+
*/
116+
public function getClassDiscriminatorMapping()
117+
{
118+
return $this->classDiscriminatorMapping;
119+
}
120+
121+
/**
122+
* {@inheritdoc}
123+
*/
124+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null)
125+
{
126+
$this->classDiscriminatorMapping = $mapping;
127+
}
128+
97129
/**
98130
* Returns the names of the properties that should be serialized.
99131
*
@@ -104,6 +136,7 @@ public function __sleep()
104136
return array(
105137
'name',
106138
'attributesMetadata',
139+
'classDiscriminatorMapping',
107140
);
108141
}
109142
}

‎src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,14 @@ public function merge(ClassMetadataInterface $classMetadata);
5454
* @return \ReflectionClass
5555
*/
5656
public function getReflectionClass();
57+
58+
/**
59+
* @return ClassDiscriminatorMapping|null
60+
*/
61+
public function getClassDiscriminatorMapping();
62+
63+
/**
64+
* @param ClassDiscriminatorMapping|null $mapping
65+
*/
66+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null);
5767
}

‎src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
namespace Symfony\Component\Serializer\Mapping\Loader;
1313

1414
use Doctrine\Common\Annotations\Reader;
15+
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
1516
use Symfony\Component\Serializer\Annotation\Groups;
1617
use Symfony\Component\Serializer\Annotation\MaxDepth;
1718
use Symfony\Component\Serializer\Exception\MappingException;
1819
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
20+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
1921
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
2022

2123
/**
@@ -43,6 +45,15 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
4345

4446
$attributesMetadata = $classMetadata->getAttributesMetadata();
4547

48+
foreach ($this->reader->getClassAnnotations($reflectionClass) as $annotation) {
49+
if ($annotation instanceof DiscriminatorMap) {
50+
$classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping(
51+
$annotation->getTypeProperty(),
52+
$annotation->getMapping()
53+
));
54+
}
55+
}
56+
4657
foreach ($reflectionClass->getProperties() as $property) {
4758
if (!isset($attributesMetadata[$property->name])) {
4859
$attributesMetadata[$property->name] = new AttributeMetadata($property->name);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.