Completed
Pull Request — 8.x-3.x (#404)
by Sebastian
03:56 queued 01:36
created

PluggableSchemaBuilder::findByDataType()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 3
nop 2
dl 0
loc 24
rs 8.6845
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
      $this->definitions = [];
88
89
      foreach ($this->pluginManagers as $manager) {
90
        foreach ($manager->getDefinitions() as $pluginId => $definition) {
91
          $this->definitions[] = [
92
            'id' => $pluginId,
93
            'type' => $definition['pluginType'],
94
            'weight' => $definition['weight'],
95
            'manager' => $manager,
96
            'definition' => $definition,
97
          ];
98
        }
99
      }
100
101
      uasort($this->definitions, '\Drupal\Component\Utility\SortArray::sortByWeightElement');
102
      $this->definitions = array_reverse($this->definitions);
103
    }
104
105
    return $this->definitions;
106
  }
107
108
  /**
109
   * Search for a specific plugin.
110
   *
111
   * @param callable $selector
112
   *   A selector callable that will be used to array_filter the list of
113
   *   plugin definitions.
114
   * @param integer[] $types
115
   *   A list of type constants.
116
   * @param bool $invert
117
   *   Invert the selector result.
118
   *
119
   * @return \object[] The list of matching plugin instances, keyed by name.
120
   *   The list of matching plugin instances, keyed by name.
121
   *
122
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
123
   */
124
  public function find(callable $selector, array $types, $invert = FALSE) {
125
    $instances = [];
126
    foreach ($this->getDefinitions() as $index => $definition) {
127
      $name = $definition['definition']['name'];
128
      if (empty($name)) {
129
        throw new InvalidPluginDefinitionException('Invalid GraphQL plugin definition. No name defined.');
130
      }
131
132
      if (!array_key_exists($name, $instances) && in_array($definition['definition']['pluginType'], $types)) {
133
        if ((($invert && !$selector($definition['definition'])) || $selector($definition['definition']))) {
134
          $instances[$name] = $this->getInstance($definition['manager'], $definition['type'], $definition['id']);
135
        }
136
      }
137
    }
138
139
    return $instances;
140
  }
141
142
  /**
143
   * Search for a specific plugin.
144
   *
145
   * @param string $name
146
   *   The specific plugin name.
147
   * @param integer[] $types
148
   *   A list of type constants.
149
   *
150
   * @return object The highest weighted plugin with a specific name.
151
   *   The highest weighted plugin with a specific name.
152
   *
153
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
154
   */
155
  public function findByName($name, array $types) {
156
    $result = $this->find(function($definition) use ($name) {
157
      return $definition['name'] === $name;
158
    }, $types);
159
160
    if (empty($result)) {
161
      throw new InvalidPluginDefinitionException(sprintf('GraphQL plugin with name %s could not be found.', $name));
162
    }
163
164
    return array_pop($result);
165
  }
166
167
  /**
168
   * Find the matching GraphQL data type for a Drupal type data identifier.
169
   *
170
   * Respects type chains. `entity:node:article` should return the
171
   * `NodeArticle` type if it is exposed or fall back to either `Node` or even
172
   * `Entity` otherwise.
173
   *
174
   * @param string $dataType
175
   *   The typed data identifier. E.g. `string` or `entity:node:article`.
176
   * @param string[] $types
177
   *   A list of type constants.
178
   *
179
   * @return object
180
   *   The matching type with the highest weight.
181
   */
182
  public function findByDataType($dataType, array $types = [
183
    GRAPHQL_UNION_TYPE_PLUGIN,
184
    GRAPHQL_TYPE_PLUGIN,
185
    GRAPHQL_INTERFACE_PLUGIN,
186
    GRAPHQL_SCALAR_PLUGIN,
187
  ]) {
188
    $chain = explode(':', $dataType);
189
190
    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...
191
      $dataType = implode(':', $chain);
192
193
      $types = $this->find(function($definition) use ($dataType) {
194
        return isset($definition['data_type']) && $definition['data_type'] == $dataType;
195
      }, $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...
196
197
      if (!empty($types)) {
198
        return array_pop($types);
199
      }
200
201
      array_pop($chain);
202
    }
203
204
    return NULL;
205
  }
206
207
  /**
208
   * Retrieve all mutations.
209
   *
210
   * @return object[]
211
   *   The list of mutation plugins.
212
   */
213
  public function getMutations() {
214
    return $this->find(function() {
215
      return TRUE;
216
    }, [GRAPHQL_MUTATION_PLUGIN]);
217
  }
218
219
  /**
220
   * Retrieve all fields that are not associated with a specific type.
221
   *
222
   * @return object[]
223
   *   The list root field plugins.
224
   */
225
  public function getRootFields() {
226
    // Retrieve the list of fields that are explicitly attached to a type.
227
    $attachedFields = array_reduce(array_filter(array_map(function($definition) {
228
      return array_key_exists('fields', $definition['definition']) ? $definition['definition']['fields'] : NULL;
229
    }, $this->getDefinitions())), 'array_merge', []);
230
231
    // Retrieve the list of fields that are not attached in any way or
232
    // explicitly attached to the artificial "Root" type.
233
    return $this->find(function($definition) use ($attachedFields) {
234
      return (!in_array($definition['name'], $attachedFields) && empty($definition['parents'])) || in_array('Root', $definition['parents']);
235
    }, [GRAPHQL_FIELD_PLUGIN]);
236
  }
237
238
  /**
239
   * Creates a type system plugin instance for a given plugin manager.
240
   *
241
   * @param \Drupal\Component\Plugin\PluginManagerInterface $manager
242
   *   The plugin manager responsible for creation of the plugin instance.
243
   * @param $pluginType
244
   *   The plugin type.
245
   * @param $pluginId
246
   *   The plugin id.
247
   *
248
   * @return \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface
249
   *   The created plugin instance.
250
   */
251
  protected function getInstance(PluginManagerInterface $manager, $pluginType, $pluginId) {
252
    if (!isset($this->instances[$pluginType][$pluginId])) {
253
      // Initialize the static cache array if necessary.
254
      $this->instances[$pluginType] = isset($this->instances[$pluginType]) ? $this->instances[$pluginType] : [];
255
256
      // We do not allow plugin configuration for now.
257
      $instance = $manager->createInstance($pluginId);
258
      if (empty($instance)) {
259
        throw new \LogicException(sprintf('Could not instantiate plugin %s of type %s.', $pluginId, $pluginType));
260
      }
261
262
      if (!$instance instanceof TypeSystemPluginInterface) {
263
        throw new \LogicException(sprintf('Plugin %s of type %s does not implement \Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface.', $pluginId, $pluginType));
264
      }
265
266
      // Prevent circular dependencies by building the type after constructing the plugin instance.
267
      $this->instances[$pluginType][$pluginId] = $instance;
268
      $this->instances[$pluginType][$pluginId]->buildConfig($this);
269
    }
270
271
    return $this->instances[$pluginType][$pluginId];
272
  }
273
}
274