Completed
Push — master ( 6fe909...aff65e )
by Adrien
01:54
created

Types::isEntity()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 2
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 = [])
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

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:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
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