Completed
Push — master ( 6fd728...1b0e9e )
by David
14s queued 11s
created

SchemaFactory::setAuthenticationService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
4
namespace TheCodingMachine\GraphQL\Controllers;
5
6
use Doctrine\Common\Annotations\AnnotationRegistry;
7
use Doctrine\Common\Annotations\CachedReader;
8
use Doctrine\Common\Annotations\Reader;
9
use Doctrine\Common\Annotations\AnnotationReader as DoctrineAnnotationReader;
10
use Doctrine\Common\Cache\ApcuCache;
11
use GraphQL\Type\SchemaConfig;
12
use Psr\Container\ContainerInterface;
13
use Psr\SimpleCache\CacheInterface;
14
use TheCodingMachine\GraphQL\Controllers\Hydrators\FactoryHydrator;
15
use TheCodingMachine\GraphQL\Controllers\Hydrators\HydratorInterface;
16
use TheCodingMachine\GraphQL\Controllers\Mappers\CompositeTypeMapper;
17
use TheCodingMachine\GraphQL\Controllers\Mappers\GlobTypeMapper;
18
use TheCodingMachine\GraphQL\Controllers\Mappers\PorpaginasTypeMapper;
19
use TheCodingMachine\GraphQL\Controllers\Mappers\RecursiveTypeMapper;
20
use TheCodingMachine\GraphQL\Controllers\Mappers\TypeMapperInterface;
21
use TheCodingMachine\GraphQL\Controllers\Reflection\CachedDocBlockFactory;
22
use TheCodingMachine\GraphQL\Controllers\Security\AuthenticationServiceInterface;
23
use TheCodingMachine\GraphQL\Controllers\Security\AuthorizationServiceInterface;
24
use TheCodingMachine\GraphQL\Controllers\Security\FailAuthenticationService;
25
use TheCodingMachine\GraphQL\Controllers\Security\FailAuthorizationService;
26
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthenticationService;
27
use TheCodingMachine\GraphQL\Controllers\Security\VoidAuthorizationService;
28
use TheCodingMachine\GraphQL\Controllers\Types\TypeResolver;
29
30
/**
31
 * A class to help getting started with GraphQLControllers.
32
 * It is in charge of creating a schema with most sensible defaults.
33
 */
34
class SchemaFactory
35
{
36
    private $controllerNamespaces = [];
37
    private $typeNamespaces = [];
38
    /**
39
     * @var QueryProviderInterface[]
40
     */
41
    private $queryProviders = [];
42
    /**
43
     * @var TypeMapperInterface[]
44
     */
45
    private $typeMappers = [];
46
    private $doctrineAnnotationReader;
47
    /**
48
     * @var HydratorInterface|null
49
     */
50
    private $hydrator;
51
    /**
52
     * @var AuthenticationServiceInterface|null
53
     */
54
    private $authenticationService;
55
    /**
56
     * @var AuthorizationServiceInterface|null
57
     */
58
    private $authorizationService;
59
    /**
60
     * @var CacheInterface
61
     */
62
    private $cache;
63
    /**
64
     * @var NamingStrategyInterface|null
65
     */
66
    private $namingStrategy;
67
    /**
68
     * @var ContainerInterface
69
     */
70
    private $container;
71
    /**
72
     * @var SchemaConfig
73
     */
74
    private $schemaConfig;
75
76
    public function __construct(CacheInterface $cache, ContainerInterface $container)
77
    {
78
        $this->cache = $cache;
79
        $this->container = $container;
80
    }
81
82
    /**
83
     * Registers a namespace that can contain GraphQL controllers.
84
     */
85
    public function addControllerNamespace(string $namespace): self
86
    {
87
        $this->controllerNamespaces[] = $namespace;
88
        return $this;
89
    }
90
91
    /**
92
     * Registers a namespace that can contain GraphQL types.
93
     */
94
    public function addTypeNamespace(string $namespace): self
95
    {
96
        $this->typeNamespaces[] = $namespace;
97
        return $this;
98
    }
99
100
    /**
101
     * Registers a query provider.
102
     */
103
    public function addQueryProvider(QueryProviderInterface $queryProvider): self
104
    {
105
        $this->queryProviders[] = $queryProvider;
106
        return $this;
107
    }
108
109
    /**
110
     * Registers a type mapper.
111
     */
112
    public function addTypeMapper(TypeMapperInterface $typeMapper): self
113
    {
114
        $this->typeMappers[] = $typeMapper;
115
        return $this;
116
    }
117
118
    public function setDoctrineAnnotationReader(Reader $annotationReader): self
119
    {
120
        $this->doctrineAnnotationReader = $annotationReader;
121
        return $this;
122
    }
123
124
    /**
125
     * Returns a cached Doctrine annotation reader.
126
     * Note: we cannot get the annotation reader service in the container as we are in a compiler pass.
127
     */
128
    private function getDoctrineAnnotationReader(): Reader
129
    {
130
        if ($this->doctrineAnnotationReader === null) {
131
            AnnotationRegistry::registerLoader('class_exists');
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\Common\Annotati...istry::registerLoader() has been deprecated: this method is deprecated and will be removed in doctrine/annotations 2.0 autoloading should be deferred to the globally registered autoloader by then. For now, use @example AnnotationRegistry::registerLoader('class_exists') ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

131
            /** @scrutinizer ignore-deprecated */ AnnotationRegistry::registerLoader('class_exists');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
132
            $doctrineAnnotationReader = new DoctrineAnnotationReader();
133
134
            if (function_exists('apcu_fetch')) {
135
                $doctrineAnnotationReader = new CachedReader($doctrineAnnotationReader, new ApcuCache(), true);
136
            }
137
138
            return $doctrineAnnotationReader;
139
        }
140
        return $this->doctrineAnnotationReader;
141
    }
142
143
    public function setHydrator(HydratorInterface $hydrator): self
144
    {
145
        $this->hydrator = $hydrator;
146
        return $this;
147
    }
148
149
    public function setAuthenticationService(AuthenticationServiceInterface $authenticationService): self
150
    {
151
        $this->authenticationService = $authenticationService;
152
        return $this;
153
    }
154
155
    public function setAuthorizationService(AuthorizationServiceInterface $authorizationService): self
156
    {
157
        $this->authorizationService = $authorizationService;
158
        return $this;
159
    }
160
161
    public function setNamingStrategy(NamingStrategyInterface $namingStrategy): self
162
    {
163
        $this->namingStrategy = $namingStrategy;
164
        return $this;
165
    }
166
167
    public function setSchemaConfig(SchemaConfig $schemaConfig): self
168
    {
169
        $this->schemaConfig = $schemaConfig;
170
        return $this;
171
    }
172
173
    public function createSchema(): Schema
174
    {
175
        $annotationReader = new AnnotationReader($this->getDoctrineAnnotationReader(), AnnotationReader::LAX_MODE);
176
        $hydrator = $this->hydrator ?: new FactoryHydrator();
177
        $authenticationService = $this->authenticationService ?: new FailAuthenticationService();
178
        $authorizationService = $this->authorizationService ?: new FailAuthorizationService();
179
        $typeResolver = new TypeResolver();
180
        $cachedDocBlockFactory = new CachedDocBlockFactory($this->cache);
181
        $namingStrategy = $this->namingStrategy ?: new NamingStrategy();
182
        $typeRegistry = new TypeRegistry();
183
184
        $fieldsBuilderFactory = new FieldsBuilderFactory($annotationReader, $hydrator, $authenticationService,
185
            $authorizationService, $typeResolver, $cachedDocBlockFactory, $namingStrategy);
186
187
        $typeGenerator = new TypeGenerator($annotationReader, $fieldsBuilderFactory, $namingStrategy, $typeRegistry, $this->container);
188
        $inputTypeUtils = new InputTypeUtils($annotationReader, $namingStrategy);
189
        $inputTypeGenerator = new InputTypeGenerator($inputTypeUtils, $fieldsBuilderFactory, $hydrator);
190
191
        $typeMappers = [];
192
193
        foreach ($this->typeNamespaces as $typeNamespace) {
194
            $typeMappers[] = new GlobTypeMapper($typeNamespace, $typeGenerator, $inputTypeGenerator, $inputTypeUtils,
195
                $this->container, $annotationReader, $namingStrategy, $this->cache);
196
        }
197
198
        foreach ($this->typeMappers as $typeMapper) {
199
            $typeMappers[] = $typeMapper;
200
        }
201
202
        if ($typeMappers === []) {
203
            throw new GraphQLException('Cannot create schema: no namespace for types found (You must call the SchemaFactory::addTypeNamespace() at least once).');
204
        }
205
206
        $typeMappers[] = new PorpaginasTypeMapper();
207
208
        $compositeTypeMapper = new CompositeTypeMapper($typeMappers);
209
        $recursiveTypeMapper = new RecursiveTypeMapper($compositeTypeMapper, $namingStrategy, $this->cache, $typeRegistry);
210
211
        $queryProviders = [];
212
        foreach ($this->controllerNamespaces as $controllerNamespace) {
213
            $queryProviders[] = new GlobControllerQueryProvider($controllerNamespace, $fieldsBuilderFactory, $recursiveTypeMapper,
214
                $this->container, $this->cache);
215
        }
216
217
        foreach ($this->queryProviders as $queryProvider) {
218
            $queryProviders[] = $queryProvider;
219
        }
220
221
        if ($queryProviders === []) {
222
            throw new GraphQLException('Cannot create schema: no namespace for controllers found (You must call the SchemaFactory::addControllerNamespace() at least once).');
223
        }
224
225
        // TODO: configure ttl for cache?
226
227
        $aggregateQueryProvider = new AggregateQueryProvider($queryProviders);
228
229
        return new Schema($aggregateQueryProvider, $recursiveTypeMapper, $typeResolver, $this->schemaConfig);
230
    }
231
}
232