1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Drupal\graphql\Plugin\GraphQL\Schemas; |
4
|
|
|
|
5
|
|
|
use Drupal\Component\Plugin\PluginBase; |
6
|
|
|
use Drupal\Core\Cache\CacheableDependencyInterface; |
7
|
|
|
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; |
8
|
|
|
use Drupal\graphql\GraphQL\Execution\ResolveContext; |
9
|
|
|
use Drupal\graphql\Plugin\FieldPluginManager; |
10
|
|
|
use Drupal\graphql\Plugin\MutationPluginManager; |
11
|
|
|
use Drupal\graphql\Plugin\SubscriptionPluginManager; |
12
|
|
|
use Drupal\graphql\Plugin\SchemaBuilderInterface; |
13
|
|
|
use Drupal\graphql\Plugin\SchemaPluginInterface; |
14
|
|
|
use Drupal\graphql\Plugin\TypePluginManagerAggregator; |
15
|
|
|
use GraphQL\Type\Definition\ObjectType; |
16
|
|
|
use GraphQL\Type\Definition\ResolveInfo; |
17
|
|
|
use GraphQL\Type\Schema; |
18
|
|
|
use GraphQL\Type\SchemaConfig; |
19
|
|
|
use Symfony\Component\DependencyInjection\ContainerInterface; |
20
|
|
|
|
21
|
|
|
abstract class SchemaPluginBase extends PluginBase implements SchemaPluginInterface, SchemaBuilderInterface, ContainerFactoryPluginInterface, CacheableDependencyInterface { |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* The field plugin manager. |
25
|
|
|
* |
26
|
|
|
* @var \Drupal\graphql\Plugin\FieldPluginManager |
27
|
|
|
*/ |
28
|
|
|
protected $fieldManager; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The mutation plugin manager. |
32
|
|
|
* |
33
|
|
|
* @var \Drupal\graphql\Plugin\MutationPluginManager |
34
|
|
|
*/ |
35
|
|
|
protected $mutationManager; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* The subscription plugin manager. |
39
|
|
|
* |
40
|
|
|
* @var \Drupal\graphql\Plugin\SubscriptionPluginManager |
41
|
|
|
*/ |
42
|
|
|
protected $subscriptionManager; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* The type manager aggregator service. |
46
|
|
|
* |
47
|
|
|
* @var \Drupal\graphql\Plugin\TypePluginManagerAggregator |
48
|
|
|
*/ |
49
|
|
|
protected $typeManagers; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* Static cache of field definitions. |
53
|
|
|
* |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
protected $fields = []; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Static cache of mutation definitions. |
60
|
|
|
* |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
protected $mutations = []; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* Static cache of subscription definitions. |
67
|
|
|
* |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
protected $subscriptions = []; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Static cache of type instances. |
74
|
|
|
* |
75
|
|
|
* @var array |
76
|
|
|
*/ |
77
|
|
|
protected $types = []; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* {@inheritdoc} |
81
|
|
|
*/ |
82
|
|
View Code Duplication |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { |
|
|
|
|
83
|
|
|
return new static( |
84
|
|
|
$configuration, |
85
|
|
|
$plugin_id, |
86
|
|
|
$plugin_definition, |
87
|
|
|
$container->get('plugin.manager.graphql.field'), |
88
|
|
|
$container->get('plugin.manager.graphql.mutation'), |
89
|
|
|
$container->get('plugin.manager.graphql.subscription'), |
90
|
|
|
$container->get('graphql.type_manager_aggregator') |
91
|
|
|
); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* SchemaPluginBase constructor. |
96
|
|
|
* |
97
|
|
|
* @param array $configuration |
98
|
|
|
* The plugin configuration array. |
99
|
|
|
* @param string $pluginId |
100
|
|
|
* The plugin id. |
101
|
|
|
* @param array $pluginDefinition |
102
|
|
|
* The plugin definition array. |
103
|
|
|
* @param \Drupal\graphql\Plugin\FieldPluginManager $fieldManager |
104
|
|
|
* The field plugin manager. |
105
|
|
|
* @param \Drupal\graphql\Plugin\MutationPluginManager $mutationManager |
106
|
|
|
* The mutation plugin manager. |
107
|
|
|
* @param \Drupal\graphql\Plugin\SubscriptionPluginManager $subscriptionManager |
108
|
|
|
* The subscription plugin manager. |
109
|
|
|
* @param \Drupal\graphql\Plugin\TypePluginManagerAggregator $typeManagers |
110
|
|
|
* The type manager aggregator service. |
111
|
|
|
*/ |
112
|
|
View Code Duplication |
public function __construct( |
|
|
|
|
113
|
|
|
$configuration, |
114
|
|
|
$pluginId, |
115
|
|
|
$pluginDefinition, |
116
|
|
|
FieldPluginManager $fieldManager, |
117
|
|
|
MutationPluginManager $mutationManager, |
118
|
|
|
SubscriptionPluginManager $subscriptionManager, |
119
|
|
|
TypePluginManagerAggregator $typeManagers |
120
|
|
|
) { |
121
|
|
|
parent::__construct($configuration, $pluginId, $pluginDefinition); |
122
|
|
|
$this->fieldManager = $fieldManager; |
123
|
|
|
$this->mutationManager = $mutationManager; |
124
|
|
|
$this->subscriptionManager = $subscriptionManager; |
125
|
|
|
$this->typeManagers = $typeManagers; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* {@inheritdoc} |
130
|
|
|
*/ |
131
|
|
|
public function getSchema() { |
132
|
|
|
$config = new SchemaConfig(); |
133
|
|
|
|
134
|
|
|
if ($this->hasMutations()) { |
135
|
|
|
$config->setMutation(new ObjectType([ |
136
|
|
|
'name' => 'MutationRoot', |
137
|
|
|
'fields' => function () { |
138
|
|
|
return $this->getMutations(); |
139
|
|
|
}, |
140
|
|
|
])); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
if ($this->hasSubscriptions()) { |
144
|
|
|
$config->setSubscription(new ObjectType([ |
145
|
|
|
'name' => 'SubscriptionRoot', |
146
|
|
|
'fields' => function () { |
147
|
|
|
return $this->getSubscriptions(); |
148
|
|
|
}, |
149
|
|
|
])); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
$config->setQuery(new ObjectType([ |
153
|
|
|
'name' => 'QueryRoot', |
154
|
|
|
'fields' => function () { |
155
|
|
|
return $this->getFields('Root'); |
156
|
|
|
}, |
157
|
|
|
])); |
158
|
|
|
|
159
|
|
|
$config->setTypes(function () { |
160
|
|
|
return $this->getTypes(); |
161
|
|
|
}); |
162
|
|
|
|
163
|
|
|
$config->setTypeLoader(function ($name) { |
164
|
|
|
return $this->getType($name); |
165
|
|
|
}); |
166
|
|
|
|
167
|
|
|
return new Schema($config); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* {@inheritdoc} |
172
|
|
|
*/ |
173
|
|
|
public function hasFields($type) { |
174
|
|
|
return isset($this->pluginDefinition['field_association_map'][$type]); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* {@inheritdoc} |
179
|
|
|
*/ |
180
|
|
|
public function hasMutations() { |
181
|
|
|
return !empty($this->pluginDefinition['mutation_map']); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* {@inheritdoc} |
186
|
|
|
*/ |
187
|
|
|
public function hasSubscriptions() { |
188
|
|
|
return !empty($this->pluginDefinition['subscription_map']); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* {@inheritdoc} |
193
|
|
|
*/ |
194
|
|
|
public function hasType($name) { |
195
|
|
|
return isset($this->pluginDefinition['type_map'][$name]); |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* {@inheritdoc} |
200
|
|
|
*/ |
201
|
|
|
public function getFields($type) { |
202
|
|
|
$association = $this->pluginDefinition['field_association_map']; |
203
|
|
|
$fields = $this->pluginDefinition['field_map']; |
204
|
|
|
|
205
|
|
|
if (isset($association[$type])) { |
206
|
|
|
return $this->processFields(array_map(function ($id) use ($fields) { |
207
|
|
|
return $fields[$id]; |
208
|
|
|
}, $association[$type])); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
return []; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* {@inheritdoc} |
216
|
|
|
*/ |
217
|
|
|
public function getMutations() { |
218
|
|
|
return $this->processMutations($this->pluginDefinition['mutation_map']); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* {@inheritdoc} |
223
|
|
|
*/ |
224
|
|
|
public function getSubscriptions() { |
225
|
|
|
return $this->processSubscriptions($this->pluginDefinition['subscription_map']); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* {@inheritdoc} |
230
|
|
|
*/ |
231
|
|
|
public function getTypes() { |
232
|
|
|
return array_map(function ($name) { |
233
|
|
|
return $this->getType($name); |
234
|
|
|
}, array_keys($this->pluginDefinition['type_map'])); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* {@inheritdoc} |
239
|
|
|
*/ |
240
|
|
|
public function getSubTypes($name) { |
241
|
|
|
$association = $this->pluginDefinition['type_association_map']; |
242
|
|
|
return isset($association[$name]) ? $association[$name] : []; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* {@inheritdoc} |
247
|
|
|
*/ |
248
|
|
|
public function resolveType($name, $value, ResolveContext $context, ResolveInfo $info) { |
249
|
|
|
$association = $this->pluginDefinition['type_association_map']; |
250
|
|
|
$types = $this->pluginDefinition['type_map']; |
251
|
|
|
if (!isset($association[$name])) { |
252
|
|
|
return NULL; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
foreach ($association[$name] as $type) { |
256
|
|
|
// TODO: Try to avoid loading the type for the check. Consider to make it static! |
257
|
|
|
if (isset($types[$type]) && $instance = $this->buildType($types[$type])) { |
258
|
|
|
if ($instance->isTypeOf($value, $context, $info)) { |
259
|
|
|
return $instance; |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
return NULL; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* {@inheritdoc} |
269
|
|
|
*/ |
270
|
|
|
public function getType($name) { |
271
|
|
|
$types = $this->pluginDefinition['type_map']; |
272
|
|
|
$references = $this->pluginDefinition['type_reference_map']; |
273
|
|
|
if (isset($types[$name])) { |
274
|
|
|
return $this->buildType($this->pluginDefinition['type_map'][$name]); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
do { |
278
|
|
|
if (isset($references[$name])) { |
279
|
|
|
return $this->buildType($types[$references[$name]]); |
280
|
|
|
} |
281
|
|
|
} while (($pos = strpos($name, ':')) !== FALSE && $name = substr($name, 0, $pos)); |
282
|
|
|
|
283
|
|
|
throw new \LogicException(sprintf('Missing type %s.', $name)); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* {@inheritdoc} |
288
|
|
|
*/ |
289
|
|
|
public function processMutations($mutations) { |
290
|
|
|
return array_map([$this, 'buildMutation'], $mutations); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* {@inheritdoc} |
295
|
|
|
*/ |
296
|
|
|
public function processSubscriptions($subscriptions) { |
297
|
|
|
return array_map([$this, 'buildSubscription'], $subscriptions); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
/** |
301
|
|
|
* {@inheritdoc} |
302
|
|
|
*/ |
303
|
|
|
public function processFields($fields) { |
304
|
|
|
return array_map([$this, 'buildField'], $fields); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* {@inheritdoc} |
309
|
|
|
*/ |
310
|
|
|
public function processArguments($args) { |
311
|
|
|
return array_map(function ($arg) { |
312
|
|
|
return [ |
313
|
|
|
'type' => $this->processType($arg['type']), |
314
|
|
|
] + $arg; |
315
|
|
|
}, $args); |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* {@inheritdoc} |
320
|
|
|
*/ |
321
|
|
|
public function processType($type) { |
322
|
|
|
list($type, $decorators) = $type; |
323
|
|
|
|
324
|
|
|
return array_reduce($decorators, function ($type, $decorator) { |
325
|
|
|
return $decorator($type); |
326
|
|
|
}, $this->getType($type)); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Retrieves the type instance for the given reference. |
331
|
|
|
* |
332
|
|
|
* @param array $type |
333
|
|
|
* The type reference. |
334
|
|
|
* |
335
|
|
|
* @return \GraphQL\Type\Definition\Type |
336
|
|
|
* The type instance. |
337
|
|
|
*/ |
338
|
|
|
protected function buildType($type) { |
339
|
|
|
if (!isset($this->types[$type['id']])) { |
340
|
|
|
$creator = [$type['class'], 'createInstance']; |
341
|
|
|
$manager = $this->typeManagers->getTypeManager($type['type']); |
342
|
|
|
$this->types[$type['id']] = $creator($this, $manager, $type['definition'], $type['id']); |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
return $this->types[$type['id']]; |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* Retrieves the field definition for a given field reference. |
350
|
|
|
* |
351
|
|
|
* @param array $field |
352
|
|
|
* The type reference. |
353
|
|
|
* |
354
|
|
|
* @return array |
355
|
|
|
* The field definition. |
356
|
|
|
*/ |
357
|
|
View Code Duplication |
protected function buildField($field) { |
|
|
|
|
358
|
|
|
if (!isset($this->fields[$field['id']])) { |
359
|
|
|
$creator = [$field['class'], 'createInstance']; |
360
|
|
|
$this->fields[$field['id']] = $creator($this, $this->fieldManager, $field['definition'], $field['id']); |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
return $this->fields[$field['id']]; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Retrieves the mutation definition for a given field reference. |
368
|
|
|
* |
369
|
|
|
* @param array $mutation |
370
|
|
|
* The mutation reference. |
371
|
|
|
* |
372
|
|
|
* @return array |
373
|
|
|
* The mutation definition. |
374
|
|
|
*/ |
375
|
|
View Code Duplication |
protected function buildMutation($mutation) { |
|
|
|
|
376
|
|
|
if (!isset($this->mutations[$mutation['id']])) { |
377
|
|
|
$creator = [$mutation['class'], 'createInstance']; |
378
|
|
|
$this->mutations[$mutation['id']] = $creator($this, $this->mutationManager, $mutation['definition'], $mutation['id']); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
return $this->mutations[$mutation['id']]; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* Retrieves the subscription definition for a given field reference. |
386
|
|
|
* |
387
|
|
|
* @param array $mutation |
|
|
|
|
388
|
|
|
* The subscription reference. |
389
|
|
|
* |
390
|
|
|
* @return array |
391
|
|
|
* The subscription definition. |
392
|
|
|
*/ |
393
|
|
View Code Duplication |
protected function buildSubscription($subscription) { |
|
|
|
|
394
|
|
|
if (!isset($this->subscriptions[$subscription['id']])) { |
395
|
|
|
$creator = [$subscription['class'], 'createInstance']; |
396
|
|
|
$this->subscriptions[$subscription['id']] = $creator($this, $this->subscriptionManager, $subscription['definition'], $subscription['id']); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
return $this->subscriptions[$subscription['id']]; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* {@inheritdoc} |
404
|
|
|
*/ |
405
|
|
|
public function getCacheContexts() { |
406
|
|
|
return []; |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* {@inheritdoc} |
411
|
|
|
*/ |
412
|
|
|
public function getCacheTags() { |
413
|
|
|
return $this->pluginDefinition['schema_cache_tags']; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
/** |
417
|
|
|
* {@inheritdoc} |
418
|
|
|
*/ |
419
|
|
|
public function getCacheMaxAge() { |
420
|
|
|
return $this->pluginDefinition['schema_cache_max_age']; |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.