1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace GraphQL\Doctrine; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Annotations\AnnotationRegistry; |
8
|
|
|
use Doctrine\ORM\EntityManager; |
9
|
|
|
use GraphQL\Doctrine\Definition\EntityIDType; |
10
|
|
|
use GraphQL\Type\Definition\Type; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Registry of types to manage all GraphQL types |
14
|
|
|
* |
15
|
|
|
* This is the entry point for the library. |
16
|
|
|
*/ |
17
|
|
|
class Types |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* @var array mapping of type name to type instances |
21
|
|
|
*/ |
22
|
|
|
private $types = []; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var ObjectTypeFactory |
26
|
|
|
*/ |
27
|
|
|
private $objectTypeFactory; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var EntityManager |
31
|
|
|
*/ |
32
|
|
|
private $entityManager; |
33
|
|
|
|
34
|
14 |
|
public function __construct(EntityManager $entityManager, array $customTypeMapping = []) |
|
|
|
|
35
|
|
|
{ |
36
|
14 |
|
$this->types = $this->getPhpToGraphQLMapping(); |
37
|
14 |
|
$this->entityManager = $entityManager; |
38
|
14 |
|
$this->objectTypeFactory = new ObjectTypeFactory($this, $entityManager); |
39
|
|
|
|
40
|
14 |
|
$entityManager->getConfiguration()->newDefaultAnnotationDriver(); |
41
|
14 |
|
AnnotationRegistry::registerLoader('class_exists'); |
42
|
|
|
|
43
|
14 |
|
foreach ($customTypeMapping as $phpType => $graphQLType) { |
44
|
14 |
|
$instance = $this->createInstance($graphQLType, false); |
45
|
14 |
|
$this->registerInstance($phpType, $instance); |
46
|
|
|
} |
47
|
14 |
|
} |
48
|
|
|
|
49
|
14 |
|
private function createInstance(string $className, bool $isInputType): Type |
50
|
|
|
{ |
51
|
14 |
|
if (is_a($className, Type::class, true)) { |
52
|
14 |
|
return new $className(); |
53
|
|
|
} |
54
|
|
|
|
55
|
10 |
|
if (!$this->isEntity($className)) { |
56
|
1 |
|
throw new \UnexpectedValueException('Given class name `' . $className . '` is not a Doctrine entity. Either register a custom GraphQL type for `' . $className . '` when instantiating `' . self::class . '`, or change the usage of that class to something else.'); |
57
|
|
|
} |
58
|
|
|
|
59
|
9 |
|
if ($isInputType) { |
60
|
1 |
|
return new EntityIDType($this->entityManager, $className); |
61
|
|
|
} |
62
|
|
|
|
63
|
9 |
|
return $this->objectTypeFactory->create($className); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Checks if a className is a valid doctrine entity |
68
|
|
|
* |
69
|
|
|
* @return bool |
70
|
|
|
*/ |
71
|
10 |
|
private function isEntity(string $className): bool |
72
|
|
|
{ |
73
|
10 |
|
return class_exists($className) && !$this->entityManager->getMetadataFactory()->isTransient($className); |
74
|
|
|
} |
75
|
|
|
|
76
|
14 |
|
private function registerInstance(string $key, Type $instance): void |
77
|
|
|
{ |
78
|
14 |
|
$this->types[$key] = $instance; |
79
|
14 |
|
$this->types[$instance->name] = $instance; |
80
|
14 |
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Always return the same instance of `Type` for the given class name |
84
|
|
|
* @param string $className the class name of either a scalar type (`PostStatus::class`), or an entity (`Post::class`) |
85
|
|
|
* @return Type |
86
|
|
|
*/ |
87
|
13 |
|
public function get(string $className, bool $isInputType = false): Type |
88
|
|
|
{ |
89
|
13 |
|
$className = ltrim($className, '\\'); |
90
|
13 |
|
$key = $isInputType && $this->isEntity($className) ? Utils::getIDTypeName($className) : $className; |
91
|
|
|
|
92
|
13 |
|
if (!isset($this->types[$key])) { |
93
|
11 |
|
$instance = $this->createInstance($className, $isInputType); |
94
|
10 |
|
$this->registerInstance($key, $instance); |
95
|
|
|
} |
96
|
|
|
|
97
|
12 |
|
return $this->types[$key]; |
98
|
|
|
} |
99
|
|
|
|
100
|
14 |
|
private function getPhpToGraphQLMapping(): array |
101
|
|
|
{ |
102
|
|
|
return [ |
103
|
14 |
|
'id' => Type::id(), |
104
|
14 |
|
'bool' => Type::boolean(), |
105
|
14 |
|
'int' => Type::int(), |
106
|
14 |
|
'float' => Type::float(), |
107
|
14 |
|
'string' => Type::string(), |
108
|
|
|
]; |
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.