Completed
Pull Request — 8.x-3.x (#404)
by Sebastian
03:02 queued 40s
created

PluggableSchemaBuilder::getSchemaConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 0
dl 0
loc 21
rs 9.3142
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\graphql\Plugin\GraphQL;
4
5
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
6
use Drupal\Component\Plugin\PluginManagerInterface;
7
use Symfony\Component\DependencyInjection\ContainerInterface;
8
use Youshido\GraphQL\Config\Schema\SchemaConfig;
9
use Youshido\GraphQL\Schema\InternalSchemaMutationObject;
10
use Youshido\GraphQL\Schema\InternalSchemaQueryObject;
11
12
class PluggableSchemaBuilder implements SchemaBuilderInterface {
13
14
  /**
15
   * Static cache of type system plugin instances.
16
   *
17
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
18
   */
19
  protected $instances = [];
20
21
  /**
22
   * Static cache of plugin definitions.
23
   *
24
   * @var array
25
   */
26
  protected $definitions;
27
28
  /**
29
   * List of registered type system plugin managers.
30
   *
31
   * @var \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator
32
   */
33
  protected $pluginManagers;
34
35
  /**
36
   * {@inheritdoc}
37
   */
38
  public static function createInstance(ContainerInterface $container, $pluginId, array $pluginDefinition) {
39
    return new static(
40
      $container->get('graphql.plugin_manager_aggregator')
41
    );
42
  }
43
44
  /**
45
   * SchemaBuilder constructor.
46
   *
47
   * @param \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginManagerAggregator $pluginManagers
48
   *   List of type system plugin managers.
49
   */
50
  public function __construct(TypeSystemPluginManagerAggregator $pluginManagers) {
51
    $this->pluginManagers = $pluginManagers;
52
  }
53
54
  /**
55
   * {@inheritdoc}
56
   */
57
  public function getSchemaConfig() {
58
    $mutation = new InternalSchemaMutationObject(['name' => 'RootMutation']);
59
    $mutation->addFields($this->getMutations());
60
61
    $query = new InternalSchemaQueryObject(['name' => 'RootQuery']);
62
    $query->addFields($this->getRootFields());
63
64
    $types = $this->find(function() {
65
      return TRUE;
66
    }, [
67
      GRAPHQL_UNION_TYPE_PLUGIN,
68
      GRAPHQL_TYPE_PLUGIN,
69
      GRAPHQL_INPUT_TYPE_PLUGIN,
70
    ]);
71
72
    return new SchemaConfig([
73
      'query' => $query,
74
      'mutation' => $mutation,
75
      'types' => $types,
76
    ]);
77
  }
78
79
  /**
80
   * Returns the list of sorted plugin definitions.
81
   *
82
   * @return array
83
   *   The list of sorted plugin definitions.
84
   */
85
  public function getDefinitions() {
86
    if (!isset($this->definitions)) {
87
      foreach ($this->pluginManagers as $manager) {
88
        foreach ($manager->getDefinitions() as $pluginId => $definition) {
89
          $this->definitions[] = [
90
            'id' => $pluginId,
91
            'type' => $definition['pluginType'],
92
            'weight' => $definition['weight'],
93
            'manager' => $manager,
94
            'definition' => $definition,
95
          ];
96
        }
97
      }
98
99
      uasort($this->definitions, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
100
      $this->definitions = array_reverse($this->definitions);
101
    }
102
103
    return $this->definitions;
104
  }
105
106
  /**
107
   * Search for a specific plugin.
108
   *
109
   * @param callable $selector
110
   *   A selector callable that will be used to array_filter the list of
111
   *   plugin definitions.
112
   * @param integer[] $types
113
   *   A list of type constants.
114
   * @param bool $invert
115
   *   Invert the selector result.
116
   *
117
   * @return \object[] The list of matching plugin instances, keyed by name.
118
   *   The list of matching plugin instances, keyed by name.
119
   *
120
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
121
   */
122
  public function find(callable $selector, array $types, $invert = FALSE) {
123
    $instances = [];
124
    foreach ($this->getDefinitions() as $index => $definition) {
125
      $name = $definition['definition']['name'];
126
      if (empty($name)) {
127
        throw new InvalidPluginDefinitionException('Invalid GraphQL plugin definition. No name defined.');
128
      }
129
130
      if (!array_key_exists($name, $instances) && in_array($definition['definition']['pluginType'], $types)) {
131
        if ((($invert && !$selector($definition['definition'])) || $selector($definition['definition']))) {
132
          $instances[$name] = $this->getInstance($definition['manager'], $definition['type'], $definition['id']);
133
        }
134
      }
135
    }
136
137
    return $instances;
138
  }
139
140
  /**
141
   * Search for a specific plugin.
142
   *
143
   * @param string $name
144
   *   The specific plugin name.
145
   * @param integer[] $types
146
   *   A list of type constants.
147
   *
148
   * @return object The highest weighted plugin with a specific name.
149
   *   The highest weighted plugin with a specific name.
150
   *
151
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
152
   */
153
  public function findByName($name, array $types) {
154
    $result = $this->find(function($definition) use ($name) {
155
      return $definition['name'] === $name;
156
    }, $types);
157
158
    if (empty($result)) {
159
      throw new InvalidPluginDefinitionException(sprintf('GraphQL plugin with name %s could not be found.', $name));
160
    }
161
162
    return array_pop($result);
163
  }
164
165
  /**
166
   * Find the matching GraphQL data type for a Drupal type data identifier.
167
   *
168
   * Respects type chains. `entity:node:article` should return the
169
   * `NodeArticle` type if it is exposed or fall back to either `Node` or even
170
   * `Entity` otherwise.
171
   *
172
   * @param string $dataType
173
   *   The typed data identifier. E.g. `string` or `entity:node:article`.
174
   * @param string[] $types
175
   *   A list of type constants.
176
   *
177
   * @return object
178
   *   The matching type with the highest weight.
179
   */
180
  public function findByDataType($dataType, array $types = [
181
    GRAPHQL_UNION_TYPE_PLUGIN,
182
    GRAPHQL_TYPE_PLUGIN,
183
    GRAPHQL_INTERFACE_PLUGIN,
184
    GRAPHQL_SCALAR_PLUGIN,
185
  ]) {
186
    $chain = explode(':', $dataType);
187
188
    while ($chain) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $chain of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
189
      $dataType = implode(':', $chain);
190
191
      $types = $this->find(function($definition) use ($dataType) {
192
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
193
      }, $types);
0 ignored issues
show
Documentation introduced by
$types is of type array<integer,object<object>|string>, but the function expects a array<integer,integer>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
194
195
      if (!empty($types)) {
196
        return array_pop($types);
197
      }
198
199
      array_pop($chain);
200
    }
201
202
    return NULL;
203
  }
204
205
  /**
206
   * Retrieve all mutations.
207
   *
208
   * @return object[]
209
   *   The list of mutation plugins.
210
   */
211
  public function getMutations() {
212
    return $this->find(function() {
213
      return TRUE;
214
    }, [GRAPHQL_MUTATION_PLUGIN]);
215
  }
216
217
  /**
218
   * Retrieve all fields that are not associated with a specific type.
219
   *
220
   * @return object[]
221
   *   The list root field plugins.
222
   */
223
  public function getRootFields() {
224
    // Retrieve the list of fields that are explicitly attached to a type.
225
    $attachedFields = array_reduce(array_filter(array_map(function($definition) {
226
      return array_key_exists('fields', $definition['definition']) ? $definition['definition']['fields'] : NULL;
227
    }, $this->getDefinitions())), 'array_merge', []);
228
229
    // Retrieve the list of fields that are not attached in any way or
230
    // explicitly attached to the artificial "Root" type.
231
    return $this->find(function($definition) use ($attachedFields) {
232
      return (!in_array($definition['name'], $attachedFields) && empty($definition['parents'])) || in_array('Root', $definition['parents']);
233
    }, [GRAPHQL_FIELD_PLUGIN]);
234
  }
235
236
  /**
237
   * Creates a type system plugin instance for a given plugin manager.
238
   *
239
   * @param \Drupal\Component\Plugin\PluginManagerInterface $manager
240
   *   The plugin manager responsible for creation of the plugin instance.
241
   * @param $pluginType
242
   *   The plugin type.
243
   * @param $pluginId
244
   *   The plugin id.
245
   *
246
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
247
   *   The created plugin instance.
248
   */
249
  protected function getInstance(PluginManagerInterface $manager, $pluginType, $pluginId) {
250
    if (!isset($this->instances[$pluginType][$pluginId])) {
251
      // Initialize the static cache array if necessary.
252
      $this->instances[$pluginType] = isset($this->instances[$pluginType]) ? $this->instances[$pluginType] : [];
253
254
      // We do not allow plugin configuration for now.
255
      $instance = $manager->createInstance($pluginId);
256
      if (empty($instance)) {
257
        throw new \LogicException(sprintf('Could not instantiate plugin %s of type %s.', $pluginId, $pluginType));
258
      }
259
260
      if (!$instance instanceof TypeSystemPluginInterface) {
261
        throw new \LogicException(sprintf('Plugin %s of type %s does not implement \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface.', $pluginId, $pluginType));
262
      }
263
264
      // Prevent circular dependencies by building the type after constructing the plugin instance.
265
      $this->instances[$pluginType][$pluginId] = $instance;
266
      $this->instances[$pluginType][$pluginId]->buildConfig($this);
267
    }
268
269
    return $this->instances[$pluginType][$pluginId];
270
  }
271
}
272