Skip to content

Commit 798d363

Browse files
james-damstraPowerKiKi
authored andcommitted
Add support for MappingDriverChain with MappingDriverChainAdapter
Fixes #2 Fixes #4 Fixes #19
1 parent f0c19bf commit 798d363

File tree

4 files changed

+242
-4
lines changed

4 files changed

+242
-4
lines changed

src/Factory/AbstractFactory.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
use Doctrine\Common\Annotations\Reader;
88
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver;
9+
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
910
use Doctrine\ORM\EntityManager;
1011
use GraphQL\Doctrine\Exception;
12+
use GraphQL\Doctrine\Factory\MetadataReader\MappingDriverChainAdapter;
1113
use GraphQL\Doctrine\Types;
1214

1315
/**
@@ -39,10 +41,14 @@ public function __construct(Types $types, EntityManager $entityManager)
3941
final protected function getAnnotationReader(): Reader
4042
{
4143
$driver = $this->entityManager->getConfiguration()->getMetadataDriverImpl();
42-
if (!$driver instanceof AnnotationDriver) {
43-
throw new Exception('graphql-doctrine requires Doctrine to be configured with a `' . AnnotationDriver::class . '`.');
44+
if ($driver instanceof AnnotationDriver) {
45+
return $driver->getReader();
4446
}
4547

46-
return $driver->getReader();
48+
if ($driver instanceof MappingDriverChain) {
49+
return new MappingDriverChainAdapter($driver);
50+
}
51+
52+
throw new Exception('graphql-doctrine requires Doctrine to be configured with a `' . AnnotationDriver::class . '`.');
4753
}
4854
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace GraphQL\Doctrine\Factory\MetadataReader;
6+
7+
use Doctrine\Common\Annotations\Reader;
8+
use Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver;
9+
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
10+
use GraphQL\Doctrine\Exception;
11+
use ReflectionClass;
12+
13+
class MappingDriverChainAdapter implements Reader
14+
{
15+
/**
16+
* @var MappingDriverChain
17+
*/
18+
private $chainDriver;
19+
20+
public function __construct(MappingDriverChain $chainDriver)
21+
{
22+
$this->chainDriver = $chainDriver;
23+
}
24+
25+
/**
26+
* Find the reader for the class
27+
*
28+
* @param ReflectionClass $class
29+
*
30+
* @return Reader
31+
*/
32+
private function findReader(ReflectionClass $class): Reader
33+
{
34+
$className = $class->getName();
35+
foreach ($this->chainDriver->getDrivers() as $namespace => $driver) {
36+
if (mb_stripos($className, $namespace) === 0) {
37+
if ($driver instanceof AnnotationDriver) {
38+
return $driver->getReader();
39+
}
40+
}
41+
}
42+
43+
if ($this->chainDriver->getDefaultDriver() instanceof AnnotationDriver) {
44+
return $this->chainDriver->getDefaultDriver()->getReader();
45+
}
46+
47+
throw new Exception('graphql-doctrine requires ' . $className . ' entity to be configured with a `' . AnnotationDriver::class . '`.');
48+
}
49+
50+
/**
51+
* Gets the annotations applied to a class.
52+
*
53+
* @param ReflectionClass $class the ReflectionClass of the class from which
54+
* the class annotations should be read
55+
*
56+
* @return array an array of Annotations
57+
*/
58+
public function getClassAnnotations(ReflectionClass $class)
59+
{
60+
return $this->findReader($class)
61+
->getClassAnnotations($class);
62+
}
63+
64+
/**
65+
* Gets a class annotation.
66+
*
67+
* @param ReflectionClass $class the ReflectionClass of the class from which
68+
* the class annotations should be read
69+
* @param string $annotationName the name of the annotation
70+
*
71+
* @return null|object the Annotation or NULL, if the requested annotation does not exist
72+
*/
73+
public function getClassAnnotation(ReflectionClass $class, $annotationName)
74+
{
75+
return $this->findReader($class)
76+
->getClassAnnotation($class, $annotationName);
77+
}
78+
79+
/**
80+
* Gets the annotations applied to a method.
81+
*
82+
* @param \ReflectionMethod $method the ReflectionMethod of the method from which
83+
* the annotations should be read
84+
*
85+
* @return array an array of Annotations
86+
*/
87+
public function getMethodAnnotations(\ReflectionMethod $method)
88+
{
89+
return $this->findReader($method->getDeclaringClass())
90+
->getMethodAnnotations($method);
91+
}
92+
93+
/**
94+
* Gets a method annotation.
95+
*
96+
* @param \ReflectionMethod $method the ReflectionMethod to read the annotations from
97+
* @param string $annotationName the name of the annotation
98+
*
99+
* @return null|object the Annotation or NULL, if the requested annotation does not exist
100+
*/
101+
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
102+
{
103+
return $this->findReader($method->getDeclaringClass())
104+
->getMethodAnnotation($method, $annotationName);
105+
}
106+
107+
/**
108+
* Gets the annotations applied to a property.
109+
*
110+
* @param \ReflectionProperty $property the ReflectionProperty of the property
111+
* from which the annotations should be read
112+
*
113+
* @return array an array of Annotations
114+
*/
115+
public function getPropertyAnnotations(\ReflectionProperty $property)
116+
{
117+
return $this->findReader($property->getDeclaringClass())
118+
->getPropertyAnnotations($property);
119+
}
120+
121+
/**
122+
* Gets a property annotation.
123+
*
124+
* @param \ReflectionProperty $property the ReflectionProperty to read the annotations from
125+
* @param string $annotationName the name of the annotation
126+
*
127+
* @return null|object the Annotation or NULL, if the requested annotation does not exist
128+
*/
129+
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
130+
{
131+
return $this->findReader($property->getDeclaringClass())
132+
->getPropertyAnnotation($property, $annotationName);
133+
}
134+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace GraphQLTests\Doctrine;
6+
7+
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
8+
use Doctrine\ORM\Mapping\Column;
9+
use Doctrine\ORM\Mapping\Entity;
10+
use GraphQL\Doctrine\Annotation\Field;
11+
use GraphQL\Doctrine\Factory\MetadataReader\MappingDriverChainAdapter;
12+
use GraphQLTests\Doctrine\Blog\Model\Post;
13+
14+
class MappingDriverChainAdapterTest extends \PHPUnit\Framework\TestCase
15+
{
16+
use TypesTrait {
17+
setUp as typeSetup;
18+
}
19+
20+
private $chainAdapter;
21+
22+
public function setUp(): void
23+
{
24+
$this->typeSetup();
25+
26+
$config = $this->entityManager->getConfiguration();
27+
$chain = new MappingDriverChain();
28+
$chain->setDefaultDriver($config->getMetadataDriverImpl());
29+
$this->chainAdapter = new MappingDriverChainAdapter($chain);
30+
}
31+
32+
public function testGetClassAnnotations(): void
33+
{
34+
self::assertNotEmpty($this->chainAdapter->getClassAnnotations(new \ReflectionClass(Post::class)));
35+
}
36+
37+
public function testGetClassAnnotation(): void
38+
{
39+
self::assertNotNull($this->chainAdapter->getClassAnnotation(new \ReflectionClass(Post::class), Entity::class));
40+
}
41+
42+
public function testGetMethodAnnotations(): void
43+
{
44+
self::assertNotEmpty($this->chainAdapter->getMethodAnnotations(new \ReflectionMethod(Post::class, 'getBody')));
45+
}
46+
47+
public function testGetMethodAnnotation(): void
48+
{
49+
self::assertNotNull($this->chainAdapter->getMethodAnnotations(new \ReflectionMethod(Post::class, 'getBody'), Field::class));
50+
}
51+
52+
public function testGetPropertyAnnotations(): void
53+
{
54+
self::assertNotEmpty($this->chainAdapter->getPropertyAnnotations(new \ReflectionProperty(Post::class, 'body')));
55+
}
56+
57+
public function testGetPropertyAnnotation(): void
58+
{
59+
self::assertNotNull($this->chainAdapter->getPropertyAnnotation(new \ReflectionProperty(Post::class, 'body'), Column::class));
60+
}
61+
}

tests/TypesTest.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use DateTime;
88
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
9+
use Doctrine\ORM\Mapping\Driver\XmlDriver;
910
use Doctrine\ORM\Tools\SchemaValidator;
1011
use GraphQL\Type\Definition\BooleanType;
1112
use GraphQL\Type\Definition\ObjectType;
@@ -113,16 +114,52 @@ public function testCanGetMappedTypesEitherByMappedPhpClassOrDirectTypeClass():
113114
self::assertSame($viaPhp, $viaType);
114115
}
115116

116-
public function testDoctrineWithoutAnnotationDriverMustThrow(): void
117+
public function testDoctrineWithMappingDriverChainUsingDefault(): void
117118
{
118119
// Replace annotation driver with a driver chain
119120
$config = $this->entityManager->getConfiguration();
120121
$chain = new MappingDriverChain();
121122
$chain->setDefaultDriver($config->getMetadataDriverImpl());
122123
$config->setMetadataDriverImpl($chain);
123124

125+
$type = $this->types->getOutput(Post::class);
126+
self::assertNotEmpty($type->getFields());
127+
}
128+
129+
public function testDoctrineWithMappingDriverChainUsingNamespace(): void
130+
{
131+
// Replace annotation driver with a driver chain
132+
$config = $this->entityManager->getConfiguration();
133+
$chain = new MappingDriverChain();
134+
$driver = $config->getMetadataDriverImpl();
135+
if ($driver === null) {
136+
self::fail('driver missing');
137+
} else {
138+
$chain->addDriver($driver, 'GraphQLTests\Doctrine\Blog\Model');
139+
$config->setMetadataDriverImpl($chain);
140+
$type = $this->types->getOutput(Post::class);
141+
self::assertNotEmpty($type->getFields());
142+
}
143+
}
144+
145+
public function testDoctrineWithMappingDriverChainMissingNamespace(): void
146+
{
147+
$config = $this->entityManager->getConfiguration();
148+
$chain = new MappingDriverChain();
149+
$type = $this->types->getOutput(Post::class);
150+
151+
$config->setMetadataDriverImpl($chain);
152+
$this->expectExceptionMessage('graphql-doctrine requires GraphQLTests\Doctrine\Blog\Model\Post entity to be configured with a `Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver`.');
153+
$type->getFields();
154+
}
155+
156+
public function testDoctrineWithoutAnnotationDriverMustThrow(): void
157+
{
158+
// Replace annotation driver with a driver chain
159+
$config = $this->entityManager->getConfiguration();
124160
$type = $this->types->getOutput(Post::class);
125161

162+
$config->setMetadataDriverImpl(new XmlDriver([]));
126163
$this->expectExceptionMessage('graphql-doctrine requires Doctrine to be configured with a `Doctrine\Common\Persistence\Mapping\Driver\AnnotationDriver`.');
127164
$type->getFields();
128165
}

0 commit comments

Comments
 (0)