Completed
Pull Request — 8.x-3.x (#497)
by Sebastian
07:34
created

FieldPluginBase   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 204
Duplicated Lines 8.82 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 18
loc 204
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 11

11 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefinition() 18 18 3
A isSecure() 0 3 2
A resolve() 0 9 4
B resolveDeferred() 0 27 5
A getCacheDependencies() 0 5 1
A getCacheCollector() 0 10 4
A commitCacheableDependency() 0 3 1
A commitCacheContexts() 0 3 1
A commitCacheTags() 0 3 1
A commitCacheMaxAge() 0 3 1
A resolveValues() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Drupal\graphql\Plugin\GraphQL\Fields;
4
5
use Drupal\Component\Plugin\PluginBase;
6
use Drupal\Core\Cache\CacheableDependencyInterface;
7
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
8
use Drupal\graphql\GraphQL\Field\Field;
9
use Drupal\graphql\GraphQL\SecureFieldInterface;
10
use Drupal\graphql\GraphQL\ValueWrapperInterface;
11
use Drupal\graphql\Plugin\GraphQL\PluggableSchemaBuilderInterface;
12
use Drupal\graphql\Plugin\GraphQL\Traits\ArgumentAwarePluginTrait;
13
use Drupal\graphql\Plugin\GraphQL\Traits\CacheablePluginTrait;
14
use Drupal\graphql\Plugin\GraphQL\Traits\NamedPluginTrait;
15
use Drupal\graphql\Plugin\GraphQL\TypeSystemPluginInterface;
16
use Youshido\GraphQL\Exception\ResolveException;
17
use Youshido\GraphQL\Execution\DeferredResolver;
18
use Youshido\GraphQL\Execution\ResolveInfo;
19
use Youshido\GraphQL\Type\ListType\ListType;
20
21
/**
22
 * Base class for field plugins.
23
 */
24
abstract class FieldPluginBase extends PluginBase implements TypeSystemPluginInterface, SecureFieldInterface {
25
  use CacheablePluginTrait;
26
  use NamedPluginTrait;
27
  use ArgumentAwarePluginTrait;
28
29
  /**
30
   * The field instance.
31
   *
32
   * @var \Drupal\graphql\GraphQL\Field\Field
33
   */
34
  protected $definition;
35
36
  /**
37
   * {@inheritdoc}
38
   */
39 View Code Duplication
  public function getDefinition(PluggableSchemaBuilderInterface $schemaBuilder) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
40
    if (!isset($this->definition)) {
41
      $definition = $this->getPluginDefinition();
42
43
      $config = [
44
        'name' => $this->buildName(),
45
        'description' => $this->buildDescription(),
46
        'type' => $this->buildType($schemaBuilder),
47
        'args' => $this->buildArguments($schemaBuilder),
48
        'isDeprecated' => !empty($definition['deprecated']),
49
        'deprecationReason' => !empty($definition['deprecated']) ? !empty($definition['deprecated']) : '',
50
      ];
51
52
      $this->definition = new Field($this, $schemaBuilder, $config);
53
    }
54
55
    return $this->definition;
56
  }
57
58
  /**
59
   * {@inheritdoc}
60
   */
61
  public function isSecure() {
62
    return isset($this->getPluginDefinition()['secure']) && $this->getPluginDefinition()['secure'];
63
  }
64
65
  /**
66
   * {@inheritdoc}
67
   */
68
  public function resolve($value, array $args, ResolveInfo $info) {
69
    // If not resolving in a trusted environment, check if the field is secure.
70
    $container = $info->getExecutionContext()->getContainer();
71
    if ($container->has('secure') && !$container->get('secure') && !$this->isSecure()) {
72
      throw new ResolveException(sprintf("Unable to resolve insecure field '%s' (%s).", $info->getField()->getName(), get_class($this)));
73
    }
74
75
    return $this->resolveDeferred([$this, 'resolveValues'], $value, $args, $info);
76
  }
77
78
  /**
79
   * {@inheritdoc}
80
   */
81
  protected function resolveDeferred(callable $callback, $value, array $args, ResolveInfo $info) {
82
    $result = $callback($value, $args, $info);
83
    if (is_callable($result)) {
84
      return new DeferredResolver(function () use ($result, $args, $info, $value) {
85
        return $this->resolveDeferred($result, $value, $args, $info);
86
      });
87
    }
88
89
    // Extract the result array and the corresponding cache dependencies.
90
    $result = iterator_to_array($result);
91
    // TODO: Remove this cleanup step before the stable release.
92
    $clean = array_map(function ($item) {
93
      return $item instanceof ValueWrapperInterface ? $item->getValue() : $item;
94
    }, $result);
95
96
    // Commit the cache dependencies into the processor's cache collector.
97
    $dependencies = $this->getCacheDependencies($result, $value, $args, $info);
98
    array_walk($dependencies, [$this->getCacheCollector($info), 'addCacheableDependency']);
99
100
    // If this is a list, return the result as an array.
101
    $type = $info->getReturnType()->getNullableType();
102
    if ($type instanceof ListType) {
103
      return $clean;
104
    }
105
106
    return !empty($clean) ? reset($clean) : NULL;
107
  }
108
109
  /**
110
   * Retrieve the list of cache dependencies for a given value and arguments.
111
   *
112
   * @param array $result
113
   *   The result of the field.
114
   * @param mixed $parent
115
   *   The parent value.
116
   * @param array $args
117
   *   The arguments passed to the field.
118
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
119
   *   The resolve info object.
120
   *
121
   * @return array
122
   *   A list of cacheable dependencies.
123
   */
124
  protected function getCacheDependencies(array $result, $parent, array $args, ResolveInfo $info) {
125
    return array_filter($result, function ($item) {
126
      return $item instanceof CacheableDependencyInterface;
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Cache\CacheableDependencyInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
127
    });
128
  }
129
130
  /**
131
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
132
   *
133
   * @return \Drupal\Core\Cache\RefinableCacheableDependencyInterface
134
   */
135
  protected function getCacheCollector(ResolveInfo $info) {
136
    $container = $info->getExecutionContext()->getContainer();
137
    if ($container->has('metadata') && $metadata = $container->get('metadata')) {
138
      if ($metadata instanceof RefinableCacheableDependencyInterface) {
0 ignored issues
show
Bug introduced by
The class Drupal\Core\Cache\Refina...ableDependencyInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
139
        return $metadata;
140
      }
141
    }
142
143
    throw new \LogicException('The processor does not support cache metadata collection.');
144
  }
145
146
  /**
147
   * Commit a cache dependency into the processor's cache collector.
148
   *
149
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
150
   *   The resolve info object.
151
   * @param mixed $object
152
   *   The object to merge into the cache metadata of the processor.
153
   *
154
   * @return \Drupal\Core\Cache\RefinableCacheableDependencyInterface
155
   *   The processor's cache metadata object.
156
   */
157
  public function commitCacheableDependency(ResolveInfo $info, $object) {
158
    return $this->getCacheCollector($info)->addCacheableDependency($object);
159
  }
160
161
  /**
162
   * Commit cache contexts into the processor's cache collector.
163
   *
164
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
165
   *   The resolve info object.
166
   * @param array $contexts
167
   *   The array of cache contexts to commit.
168
   *
169
   * @return \Drupal\Core\Cache\RefinableCacheableDependencyInterface
170
   *   The processor's cache metadata object.
171
   */
172
  public function commitCacheContexts(ResolveInfo $info, array $contexts) {
173
    return $this->getCacheCollector($info)->addCacheContexts($contexts);
174
  }
175
176
  /**
177
   * Commit cache tags into the processor's cache collector.
178
   *
179
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
180
   *   The resolve info object.
181
   * @param array $tags
182
   *   The array of cache tags to commit.
183
   *
184
   * @return \Drupal\Core\Cache\RefinableCacheableDependencyInterface
185
   *   The processor's cache metadata object.
186
   */
187
  public function commitCacheTags(ResolveInfo $info, array $tags) {
188
    return $this->getCacheCollector($info)->addCacheTags($tags);
189
  }
190
191
  /**
192
   * Commit a cache max age into the processor's cache collector.
193
   *
194
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
195
   *   The resolve info object.
196
   * @param int $age
197
   *   The max age to set.
198
   *
199
   * @return \Drupal\Core\Cache\RefinableCacheableDependencyInterface
200
   *   The processor's cache metadata object.
201
   */
202
  public function commitCacheMaxAge(ResolveInfo $info, $age) {
203
    return $this->getCacheCollector($info)->mergeCacheMaxAge($age);
204
  }
205
206
  /**
207
   * Retrieve the list of field values.
208
   *
209
   * Always returns a list of field values. Even for single value fields.
210
   * Single/multi field handling is responsibility of the base class.
211
   *
212
   * @param mixed $value
213
   *   The current object value.
214
   * @param array $args
215
   *   Field arguments.
216
   * @param \Youshido\GraphQL\Execution\ResolveInfo $info
217
   *   The resolve info object.
218
   *
219
   * @return \Generator
220
   *   The value generator.
221
   */
222
  protected function resolveValues($value, array $args, ResolveInfo $info) {
223
    // Allow overriding this class without having to declare this method.
224
    yield NULL;
225
  }
226
227
}
228