ViewDeriverHelperTrait   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 370
Duplicated Lines 3.78 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 0
Metric Value
wmc 49
lcom 2
cbo 1
dl 14
loc 370
rs 8.48
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getContextualArguments() 0 11 2
A getSortArguments() 0 14 3
A getFilterArguments() 0 11 4
A getPagerArguments() 0 9 2
B getTypes() 0 29 6
A isPaged() 0 7 2
A getViewDisplay() 0 5 1
A getPagerLimit() 7 7 2
A getPagerOffset() 7 7 2
A interfaceExists() 0 5 1
C getRowResolveType() 0 41 12
A getViewStyle() 0 5 1
A getCacheMetadataDefinition() 0 15 2
B getArgumentsInfo() 0 36 9

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ViewDeriverHelperTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ViewDeriverHelperTrait, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Drupal\graphql_views;
4
5
use Drupal\Component\Plugin\PluginManagerInterface;
6
use Drupal\Component\Utility\NestedArray;
7
use Drupal\graphql\Utility\StringHelper;
8
use Drupal\graphql_views\Plugin\views\row\GraphQLEntityRow;
9
use Drupal\graphql_views\Plugin\views\row\GraphQLFieldRow;
10
use Drupal\views\Plugin\views\display\DisplayPluginInterface;
11
use Drupal\views\ViewEntityInterface;
12
13
/**
14
 * Helper functions fot view derivers.
15
 */
16
trait ViewDeriverHelperTrait {
17
18
  /**
19
   * Helper function to return the contextual filter argument if any exist.
20
   *
21
   * @param array $arguments
22
   *   The array of available arguments.
23
   * @param string $id
24
   *   The plugin derivative id.
25
   *
26
   * @return array
27
   *   The contextual filter argument if applicable.
28
   */
29
  protected function getContextualArguments(array $arguments, $id) {
30
    if (!empty($arguments)) {
31
      return [
32
        'contextualFilter' => [
33
          'type' => StringHelper::camelCase($id, 'contextual', 'filter', 'input'),
34
        ],
35
      ];
36
    }
37
38
    return [];
39
  }
40
41
  /**
42
   * Helper function to retrieve the sort arguments if any are exposed.
43
   *
44
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
45
   *   The display plugin.
46
   * @param string $id
47
   *   The plugin derivative id.
48
   *
49
   * @return array
50
   *   The sort arguments if any exposed sorts are available.
51
   */
52
  protected function getSortArguments(DisplayPluginInterface $display, $id) {
53
    $sorts = array_filter($display->getOption('sorts') ?: [], function ($sort) {
54
      return $sort['exposed'];
55
    });
56
    return $sorts ? [
57
      'sortDirection' => [
58
        'type' => 'ViewSortDirection',
59
        'default' => 'asc',
60
      ],
61
      'sortBy' => [
62
        'type' => StringHelper::camelCase($id, 'sort', 'by'),
63
      ],
64
    ] : [];
65
  }
66
67
  /**
68
   * Helper function to return the filter argument if applicable.
69
   *
70
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
71
   *   The display plugin.
72
   * @param string $id
73
   *   The plugin derivative id.
74
   *
75
   * @return array
76
   *   The filter argument if any exposed filters are available.
77
   */
78
  protected function getFilterArguments(DisplayPluginInterface $display, $id) {
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
79
    $filters = array_filter($display->getOption('filters') ?: [], function ($filter) {
80
      return array_key_exists('exposed', $filter) && $filter['exposed'];
81
    });
82
83
    return !empty($filters) ? [
84
      'filter' => [
85
        'type' => $display->getGraphQLFilterInputName(),
86
      ],
87
    ] : [];
88
  }
89
90
  /**
91
   * Helper function to retrieve the pager arguments if the display is paged.
92
   *
93
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
94
   *   The display plugin.
95
   *
96
   * @return array
97
   *   An array of pager arguments if the view display is paged.
98
   */
99
  protected function getPagerArguments(DisplayPluginInterface $display) {
100
    return $this->isPaged($display) ? [
101
      'page' => ['type' => 'Int', 'default' => $this->getPagerOffset($display)],
102
      'pageSize' => [
103
        'type' => 'Int',
104
        'default' => $this->getPagerLimit($display),
105
      ],
106
    ] : [];
107
  }
108
109
  /**
110
   * Helper function to retrieve the types that the view can be attached to.
111
   *
112
   * @param array $arguments
113
   *   An array containing information about the available arguments.
114
   * @param array $types
115
   *   Types where it needs to be added.
116
   *
117
   * @return array
118
   *   An array of additional types the view can be embedded in.
119
   */
120
  protected function getTypes(array $arguments, array $types = ['Root']) {
121
122
    if (empty($arguments)) {
123
      return $types;
124
    }
125
126
    foreach ($arguments as $argument) {
127
      // Depending on whether bundles are known, we expose the view field
128
      // either on the interface (e.g. Node) or on the type (e.g. NodePage)
129
      // level. Here we specify types managed by other graphql_* modules,
130
      // yet we don't define these modules as dependencies. If types are not
131
      // in the schema, the resulting GraphQL field will be attached to
132
      // nowhere, so it won't go into the schema.
133
      if (empty($argument['bundles']) && empty($argument['entity_type'])) {
134
        continue;
135
      }
136
137
      if (empty($argument['bundles'])) {
138
        $types = array_merge($types, [StringHelper::camelCase($argument['entity_type'])]);
139
      }
140
      else {
141
        $types = array_merge($types, array_map(function ($bundle) use ($argument) {
142
          return StringHelper::camelCase($argument['entity_type'], $bundle);
143
        }, array_keys($argument['bundles'])));
144
      }
145
    }
146
147
    return $types;
148
  }
149
150
  /**
151
   * Check if a pager is configured.
152
   *
153
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
154
   *   The display configuration.
155
   *
156
   * @return bool
157
   *   Flag indicating if the view is configured with a pager.
158
   */
159
  protected function isPaged(DisplayPluginInterface $display) {
160
    $pagerOptions = $display->getOption('pager');
161
    return isset($pagerOptions['type']) && in_array($pagerOptions['type'], [
162
        'full',
163
        'mini',
164
      ]);
165
  }
166
167
  /**
168
   * Returns a view display object.
169
   *
170
   * @param \Drupal\views\ViewEntityInterface $view
171
   *   The view object.
172
   * @param string $displayId
173
   *   The display ID to use.
174
   *
175
   * @return \Drupal\views\Plugin\views\display\DisplayPluginInterface
176
   *   The view display object.
177
   */
178
  protected function getViewDisplay(ViewEntityInterface $view, $displayId) {
179
    $viewExecutable = $view->getExecutable();
180
    $viewExecutable->setDisplay($displayId);
181
    return $viewExecutable->getDisplay();
182
  }
183
184
  /**
185
   * Get the configured default limit.
186
   *
187
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
188
   *   The display configuration.
189
   *
190
   * @return int
191
   *   The default limit.
192
   */
193 View Code Duplication
  protected function getPagerLimit(DisplayPluginInterface $display) {
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...
194
    $pagerOptions = $display->getOption('pager');
195
    return NestedArray::getValue($pagerOptions, [
196
      'options',
197
      'items_per_page',
198
    ]) ?: 0;
199
  }
200
201
  /**
202
   * Get the configured default offset.
203
   *
204
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
205
   *   The display configuration.
206
   *
207
   * @return int
208
   *   The default offset.
209
   */
210 View Code Duplication
  protected function getPagerOffset(DisplayPluginInterface $display) {
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...
211
    $pagerOptions = $display->getOption('pager');
212
    return NestedArray::getValue($pagerOptions, [
213
      'options',
214
      'offset',
215
    ]) ?: 0;
216
  }
217
218
  /**
219
   * Check if a certain interface exists.
220
   *
221
   * @param string $interface
222
   *   The GraphQL interface name.
223
   * @param \Drupal\Component\Plugin\PluginManagerInterface $interfacePluginManager
224
   *   Plugin interface manager.
225
   *
226
   * @return bool
227
   *   Boolean flag indicating if the interface exists.
228
   */
229
  protected function interfaceExists($interface, PluginManagerInterface $interfacePluginManager) {
230
    return (bool) array_filter($interfacePluginManager->getDefinitions(), function ($definition) use ($interface) {
231
      return $definition['name'] === $interface;
232
    });
233
  }
234
235
  /**
236
   * Retrieves the type the view's rows resolve to.
237
   *
238
   * @param \Drupal\views\ViewEntityInterface $view
239
   *   The view entity.
240
   * @param string $displayId
241
   *   The id of the current display.
242
   * @param \Drupal\Component\Plugin\PluginManagerInterface $interfacePluginManager
243
   *   Interface plugin manager.
244
   *
245
   * @return null|string
246
   *   The name of the type or NULL if the type could not be derived.
247
   */
248
  protected function getRowResolveType(ViewEntityInterface $view, $displayId, PluginManagerInterface $interfacePluginManager) {
249
    /** @var \Drupal\graphql_views\Plugin\views\display\GraphQL $display */
250
    $display = $this->getViewDisplay($view, $displayId);
251
    $rowPlugin = $display->getPlugin('row');
252
253
    if ($rowPlugin instanceof GraphQLFieldRow) {
254
      return StringHelper::camelCase($display->getGraphQLRowName());
255
    }
256
257
    if ($rowPlugin instanceof GraphQLEntityRow) {
258
      $executable = $view->getExecutable();
259
      $executable->setDisplay($displayId);
260
261
      if ($entityType = $executable->getBaseEntityType()) {
262
        $typeName = $entityType->id();
263
        $typeNameCamel = StringHelper::camelCase($typeName);
264
        if ($this->interfaceExists($typeNameCamel, $interfacePluginManager)) {
265
          $filters = $executable->getDisplay()->getOption('filters');
266
          $dataTable = $entityType->getDataTable();
267
          $bundleKey = $entityType->getKey('bundle');
268
269
          foreach ($filters as $filter) {
270
            $isBundleFilter = $filter['table'] == $dataTable && $filter['field'] == $bundleKey;
271
            $isSingleValued = is_array($filter['value']) && count($filter['value']) == 1;
272
            $isExposed = isset($filter['exposed']) && $filter['exposed'];
273
            if ($isBundleFilter && $isSingleValued && !$isExposed) {
274
              $bundle = reset($filter['value']);
275
              $typeName .= "_$bundle";
276
              break;
277
            }
278
          }
279
280
          return StringHelper::camelCase($typeName);
281
        }
282
      }
283
284
      return 'Entity';
285
    }
286
287
    return NULL;
288
  }
289
290
  /**
291
   * Returns a view style object.
292
   *
293
   * @param \Drupal\views\ViewEntityInterface $view
294
   *   The view object.
295
   * @param string $displayId
296
   *   The display ID to use.
297
   *
298
   * @return \Drupal\views\Plugin\views\style\StylePluginBase
299
   *   The view style object.
300
   */
301
  protected function getViewStyle(ViewEntityInterface $view, $displayId) {
302
    $viewExecutable = $view->getExecutable();
303
    $viewExecutable->setDisplay($displayId);
304
    return $viewExecutable->getStyle();
305
  }
306
307
  /**
308
   * Returns cache metadata plugin definitions.
309
   *
310
   * @param \Drupal\views\ViewEntityInterface $view
311
   *   The view object.
312
   * @param \Drupal\views\Plugin\views\display\DisplayPluginInterface $display
313
   *   The view display.
314
   *
315
   * @return array
316
   *   The cache metadata definitions for the plugin definition.
317
   */
318
  protected function getCacheMetadataDefinition(ViewEntityInterface $view, DisplayPluginInterface $display) {
319
    $metadata = $display->getCacheMetadata()
320
      ->addCacheTags($view->getCacheTags())
321
      ->addCacheContexts($view->getCacheContexts())
322
      ->mergeCacheMaxAge($view->getCacheMaxAge());
323
324
    return [
325
      'schema_cache_tags' => $metadata->getCacheTags(),
326
      'schema_cache_max_age' => $metadata->getCacheMaxAge(),
327
      'response_cache_contexts' => array_filter($metadata->getCacheContexts(), function ($context) {
328
        // Don't emit the url cache contexts.
329
        return $context !== 'url' && strpos($context, 'url.') !== 0;
330
      }),
331
    ];
332
  }
333
334
  /**
335
   * Returns information about view arguments (contextual filters).
336
   *
337
   * @param array $viewArguments
338
   *   The "arguments" option of a view display.
339
   *
340
   * @return array
341
   *   Arguments information keyed by the argument ID. Subsequent array keys:
342
   *     - index: argument index.
343
   *     - entity_type: target entity type.
344
   *     - bundles: target bundles (can be empty).
345
   *
346
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
347
   */
348
  protected function getArgumentsInfo(array $viewArguments) {
349
    $argumentsInfo = [];
350
    /* @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager */
351
    $entityTypeManager = \Drupal::service('entity_type.manager');
352
353
    $index = 0;
354
    foreach ($viewArguments as $argumentId => $argument) {
355
      $info = [
356
        'index' => $index,
357
        'entity_type' => NULL,
358
        'bundles' => [],
359
      ];
360
361
      if (isset($argument['entity_type']) && isset($argument['entity_field'])) {
362
        $entityType = $entityTypeManager->getDefinition($argument['entity_type']);
363
        if ($entityType) {
364
          $idField = $entityType->getKey('id');
365
          if ($idField === $argument['entity_field']) {
366
            $info['entity_type'] = $argument['entity_type'];
367
            if (
368
              $argument['specify_validation'] &&
369
              strpos($argument['validate']['type'], 'entity:') === 0 &&
370
              !empty($argument['validate_options']['bundles'])
371
            ) {
372
              $info['bundles'] = $argument['validate_options']['bundles'];
373
            }
374
          }
375
        }
376
      }
377
378
      $argumentsInfo[$argumentId] = $info;
379
      $index++;
380
    }
381
382
    return $argumentsInfo;
383
  }
384
385
}
386