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 24c1e9e

Browse filesBrowse files
committed
Add discriminator map within the class metadata and load the annotation
1 parent eab4a89 commit 24c1e9e
Copy full SHA for 24c1e9e

10 files changed

+268
-5
lines changed
+70Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 (!isset($data['typeProperty']) || !$data['typeProperty']) {
44+
throw new InvalidArgumentException(sprintf('Parameter "typeProperty" of annotation "%s" cannot be empty.', get_class($this)));
45+
}
46+
47+
if (!isset($data['mapping']) || 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+
/**
56+
* @return string
57+
*/
58+
public function getTypeProperty()
59+
{
60+
return $this->typeProperty;
61+
}
62+
63+
/**
64+
* @return array
65+
*/
66+
public function getMapping()
67+
{
68+
return $this->mapping;
69+
}
70+
}
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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+
26+
/**
27+
* @param ClassMetadataFactoryInterface $classMetadataFactory
28+
*/
29+
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
30+
{
31+
$this->classMetadataFactory = $classMetadataFactory;
32+
}
33+
34+
/**
35+
* {@inheritdoc}
36+
*/
37+
public function getMappingForClass($class)
38+
{
39+
if ($this->classMetadataFactory->hasMetadataFor($class)) {
40+
return $this->classMetadataFactory->getMetadataFor($class)->getClassDiscriminatorMapping();
41+
}
42+
43+
return null;
44+
}
45+
46+
/**
47+
* {@inheritdoc}
48+
*/
49+
public function getMappingForMappedObject($object)
50+
{
51+
if ($this->classMetadataFactory->hasMetadataFor($object)) {
52+
$metadata = $this->classMetadataFactory->getMetadataFor($object);
53+
54+
if (null !== $metadata->getClassDiscriminatorMapping()) {
55+
return $metadata->getClassDiscriminatorMapping();
56+
}
57+
}
58+
59+
$reflectionClass = new \ReflectionClass($object);
60+
if ($parentClass = $reflectionClass->getParentClass()) {
61+
return $this->getMappingForMappedObject($parentClass->getName());
62+
}
63+
64+
return null;
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public function getTypeForMappedObject($object)
71+
{
72+
if (null === ($mapping = $this->getMappingForMappedObject($object))) {
73+
return null;
74+
}
75+
76+
return $mapping->getMappedObjectType($object);
77+
}
78+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorMapping.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public function getClassForType($type)
5252
}
5353

5454
/**
55-
* @param object $object
55+
* @param object|string $object
5656
*
5757
* @return string|null
5858
*/

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/ClassDiscriminatorResolverInterface.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ interface ClassDiscriminatorResolverInterface
2626
public function getMappingForClass($class);
2727

2828
/**
29-
* @param object $object
29+
* @param object|string $object
3030
*
3131
* @return ClassDiscriminatorMapping|null
3232
*/
3333
public function getMappingForMappedObject($object);
3434

3535
/**
36-
* @param object $object
36+
* @param object|string $object
3737
*
3838
* @return string|null
3939
*/

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/ClassMetadata.php
+30-2Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,25 @@ class ClassMetadata implements ClassMetadataInterface
4141
*/
4242
private $reflClass;
4343

44+
/**
45+
* @var ClassDiscriminatorMapping|null
46+
*
47+
* @internal This property is public in order to reduce the size of the
48+
* class' serialized representation. Do not access it. Use
49+
* {@link getClassDiscriminatorMapping()} instead.
50+
*/
51+
public $classDiscriminatorMapping;
52+
4453
/**
4554
* Constructs a metadata for the given class.
4655
*
47-
* @param string $class
56+
* @param string $class
57+
* @param ClassDiscriminatorMapping|null $classDiscriminatorMapping
4858
*/
49-
public function __construct($class)
59+
public function __construct($class, ClassDiscriminatorMapping $classDiscriminatorMapping = null)
5060
{
5161
$this->name = $class;
62+
$this->classDiscriminatorMapping = $classDiscriminatorMapping;
5263
}
5364

5465
/**
@@ -101,6 +112,22 @@ public function getReflectionClass()
101112
return $this->reflClass;
102113
}
103114

115+
/**
116+
* {@inheritdoc}
117+
*/
118+
public function getClassDiscriminatorMapping()
119+
{
120+
return $this->classDiscriminatorMapping;
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null)
127+
{
128+
$this->classDiscriminatorMapping = $mapping;
129+
}
130+
104131
/**
105132
* Returns the names of the properties that should be serialized.
106133
*
@@ -111,6 +138,7 @@ public function __sleep()
111138
return array(
112139
'name',
113140
'attributesMetadata',
141+
'classDiscriminatorMapping',
114142
);
115143
}
116144
}

‎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
@@ -58,4 +58,14 @@ public function merge(ClassMetadataInterface $classMetadata);
5858
* @return \ReflectionClass
5959
*/
6060
public function getReflectionClass();
61+
62+
/**
63+
* @return ClassDiscriminatorMapping|null
64+
*/
65+
public function getClassDiscriminatorMapping();
66+
67+
/**
68+
* @param ClassDiscriminatorMapping|null $mapping
69+
*/
70+
public function setClassDiscriminatorMapping(ClassDiscriminatorMapping $mapping = null);
6171
}

‎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
/**
@@ -49,6 +51,15 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
4951

5052
$attributesMetadata = $classMetadata->getAttributesMetadata();
5153

54+
foreach ($this->reader->getClassAnnotations($reflectionClass) as $annotation) {
55+
if ($annotation instanceof DiscriminatorMap) {
56+
$classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping(
57+
$annotation->getTypeProperty(),
58+
$annotation->getMapping()
59+
));
60+
}
61+
}
62+
5263
foreach ($reflectionClass->getProperties() as $property) {
5364
if (!isset($attributesMetadata[$property->name])) {
5465
$attributesMetadata[$property->name] = new AttributeMetadata($property->name);

‎src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummy.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/Fixtures/AbstractDummy.php
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111

1212
namespace Symfony\Component\Serializer\Tests\Fixtures;
1313

14+
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
15+
16+
/**
17+
* @DiscriminatorMap(typeProperty="type", mapping={
18+
* "first"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild",
19+
* "second"="Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild"
20+
* })
21+
*/
1422
abstract class AbstractDummy
1523
{
1624
public $foo;

‎src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
use Doctrine\Common\Annotations\AnnotationReader;
1515
use PHPUnit\Framework\TestCase;
16+
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
17+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
1618
use Symfony\Component\Serializer\Mapping\ClassMetadata;
1719
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
1820
use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory;
@@ -52,6 +54,22 @@ public function testLoadGroups()
5254
$this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata);
5355
}
5456

57+
public function testLoadDiscriminatorMap()
58+
{
59+
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy');
60+
$this->loader->loadClassMetadata($classMetadata);
61+
62+
$expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\AbstractDummy', new ClassDiscriminatorMapping('type', array(
63+
'first' => 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummyFirstChild',
64+
'second' => 'Symfony\Component\Serializer\Tests\Fixtures\AbstractDummySecondChild',
65+
)));
66+
67+
$expected->addAttributeMetadata(new AttributeMetadata('foo'));
68+
$expected->getReflectionClass();
69+
70+
$this->assertEquals($expected, $classMetadata);
71+
}
72+
5573
public function testLoadMaxDepth()
5674
{
5775
$classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy');

‎src/Symfony/Component/Serializer/Tests/SerializerTest.php

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

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
1516
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
1617
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolver;
18+
use Symfony\Component\Serializer\Mapping\ClassMetadata;
19+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
1720
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
1821
use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
1922
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
@@ -384,6 +387,43 @@ public function testSerializeAnObjectPartOfAnAbstractDefinition()
384387

385388
$this->assertEquals($jsonData, $serializer->serialize($example, 'json'));
386389
}
390+
391+
public function testDeserializeAndSerializeAbstractObjectsWithTheClassMetadataDiscriminatorResolver()
392+
{
393+
$example = new AbstractDummyFirstChild('foo-value', 'bar-value');
394+
395+
$loaderMock = $this->getMockBuilder(ClassMetadataFactoryInterface::class)->getMock();
396+
$loaderMock->method('hasMetadataFor')->will($this->returnValueMap(array(
397+
array(
398+
AbstractDummy::class,
399+
true,
400+
),
401+
)));
402+
403+
$loaderMock->method('getMetadataFor')->will($this->returnValueMap(array(
404+
array(
405+
AbstractDummy::class,
406+
new ClassMetadata(
407+
AbstractDummy::class,
408+
new ClassDiscriminatorMapping('type', array(
409+
'first' => AbstractDummyFirstChild::class,
410+
'second' => AbstractDummySecondChild::class,
411+
))
412+
),
413+
),
414+
)));
415+
416+
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
417+
$serializer = new Serializer(array(new ObjectNormalizer(null, null, null, null, $discriminatorResolver)), array('json' => new JsonEncoder()));
418+
419+
$jsonData = '{"type":"first","bar":"bar-value","foo":"foo-value"}';
420+
421+
$deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json');
422+
$this->assertEquals($example, $deserialized);
423+
424+
$serialized = $serializer->serialize($deserialized, 'json');
425+
$this->assertEquals($jsonData, $serialized);
426+
}
387427
}
388428

389429
class Model

0 commit comments

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