Passed
Pull Request — 8.x-1.x (#11)
by Frédéric G.
01:11
created

Integrity::checkEntityReference()   C

Complexity

Conditions 13
Paths 55

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 50
rs 6.6166
cc 13
nc 55
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Drupal\qa\Plugin\QaCheck\References;
6
7
use Drupal\Core\Config\ConfigFactoryInterface;
0 ignored issues
show
Bug introduced by
The type Drupal\Core\Config\ConfigFactoryInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Drupal\Core\Entity\EntityTypeManagerInterface;
0 ignored issues
show
Bug introduced by
The type Drupal\Core\Entity\EntityTypeManagerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
0 ignored issues
show
Bug introduced by
The type Drupal\Core\Field\Plugin...ype\EntityReferenceItem was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Drupal\qa\Pass;
11
use Drupal\qa\Plugin\QaCheckBase;
12
use Drupal\qa\Plugin\QaCheckInterface;
13
use Drupal\qa\Result;
14
use Symfony\Component\DependencyInjection\ContainerInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Depend...tion\ContainerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
16
/**
17
 * Integrity checks for broken entity references.
18
 *
19
 * It covers core entity_reference only.
20
 *
21
 * Future versions are expected to cover
22
 * - core: file
23
 * - core: image
24
 * - contrib: entity_reference_revisions, notably used by paragraphs.
25
 * - contrib: dynamic_entity_reference
26
 *
27
 * For entity_reference_revisions, it will check references both up and down
28
 * the
29
 * parent/child chain.
30
 *
31
 * @QaCheck(
32
 *   id = "references.integrity",
33
 *   label=@Translation("Referential integrity"),
34
 *   details=@Translation("This check finds broken entity references. Missing
35
 *   nodes or references mean broken links and a bad user experience. These
36
 *   should usually be edited."), usesBatch=true, steps=3,
37
 * )
38
 */
39
class Integrity extends QaCheckBase implements QaCheckInterface {
40
  const NAME = 'references.integrity';
41
42
  const STEP_ER = 'entity_reference';
43
  const STEP_FILE = 'file';
44
  const STEP_IMAGE = 'image';
45
  const STEP_ERR = 'entity_reference_revisions';
46
  const STEP_DER = 'dynamic_entity_reference';
47
48
  /**
49
   * The config.factory service.
50
   *
51
   * @var \Drupal\Core\Config\ConfigFactoryInterface
52
   */
53
  protected $config;
54
55
  /**
56
   * The entity_type.manager service.
57
   *
58
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
59
   */
60
  protected $etm;
61
62
  /**
63
   * A map of storage handler by entity_type ID.
64
   *
65
   * @var array
66
   */
67
  protected $storages;
68
69
  /**
70
   * SystemUnusedExtensions constructor.
71
   *
72
   * @param array $configuration
73
   *   The plugin configuration.
74
   * @param string $id
75
   *   The plugin ID.
76
   * @param array $definition
77
   *   The plugin definition.
78
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $etm
79
   *   The entity_type.manager service.
80
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
81
   *   The config.factory service.
82
   */
83
  public function __construct(
84
    array $configuration,
85
    string $id,
86
    array $definition,
87
    EntityTypeManagerInterface $etm,
88
    ConfigFactoryInterface $config
89
  ) {
90
    parent::__construct($configuration, $id, $definition);
91
    $this->config = $config;
92
    $this->etm = $etm;
93
94
    $this->cacheStorages();
95
  }
96
97
  /**
98
   * {@inheritdoc}
99
   */
100
  public static function create(
101
    ContainerInterface $container,
102
    array $configuration,
103
    $id,
104
    $definition
105
  ) {
106
    $etm = $container->get('entity_type.manager');
107
    $config = $container->get('config.factory');
108
    return new static($configuration, $id, $definition, $etm, $config);
109
  }
110
111
  /**
112
   * Fetch and cache the storage handlers per entity type for repeated use.
113
   *
114
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
115
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
116
   */
117
  protected function cacheStorages(): void {
118
    $ets = array_keys($this->etm->getDefinitions());
119
    $handlers = [];
120
    foreach ($ets as $et) {
121
      $handlers[$et] = $this->etm->getStorage($et);
122
    }
123
    $this->storages = $handlers;
124
  }
125
126
  /**
127
   * Build a map of entity_reference fields per entity_type.
128
   *
129
   * @return array
130
   *   The map.
131
   */
132
  protected function getEntityReferenceFields(): array {
133
    $fscStorage = $this->storages['field_storage_config'];
134
    $defs = $fscStorage->loadMultiple();
135
    $fields = [];
136
    /** @var \Drupal\field\FieldStorageConfigInterface $fsc */
137
    foreach ($defs as $fsc) {
138
      if ($fsc->getType() !== self::STEP_ER) {
139
        continue;
140
      }
141
      $et = $fsc->getTargetEntityTypeId();
142
      $name = $fsc->getName();
143
      $target = $fsc->getSetting('target_type');
144
      // XXX hard-coded knowledge. Maybe refactor once multiple types are used.
145
      // $prop = $fsc->getMainPropertyName();
146
      if (!isset($fields[$et])) {
147
        $fields[$et] = [];
148
      }
149
      $fields[$et][$name] = $target;
150
    }
151
    return $fields;
152
  }
153
154
  /**
155
   * Verifies integrity of entity_reference forward links.
156
   *
157
   * @return \Drupal\qa\Result
158
   *   The sub-check results.
159
   */
160
  public function checkEntityReference(): Result {
161
    $fieldMap = $this->getEntityReferenceFields();
162
    $checks = [];
163
    foreach ($fieldMap as $et => $fields) {
164
      $checks[$et] = [
165
        // <id> => [ <field_name> => <target_id> ],
166
      ];
167
      $entities = $this->storages[$et]->loadMultiple();
168
      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
169
      foreach ($entities as $entity) {
170
        $checks[$et][$entity->id()] = [];
171
        foreach ($fields as $name => $targetET) {
172
          if (!$entity->hasField($name)) {
173
            continue;
174
          }
175
          $target = $entity->get($name);
176
          if ($target->isEmpty()) {
177
            continue;
178
          }
179
          $checks[$et][$entity->id()][$name] = [];
180
          /** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $value */
181
          foreach ($target as $delta => $value) {
182
            $targetID = $value->toArray()[EntityReferenceItem::mainPropertyName()];
183
            foreach ($entity->referencedEntities() as $targetEntity) {
184
              $x = $targetEntity->getEntityTypeId();
185
              if ($x != $targetET) {
186
                continue;
187
              }
188
              // Target found, next delta.
189
              $x = $targetEntity->id();
190
              if ($x === $targetID) {
191
                continue 2;
192
              }
193
            }
194
            // Target not found: broken reference.
195
            $checks[$et][$entity->id()][$name][$delta] = $targetID;
196
          }
197
          if (empty($checks[$et][$entity->id()][$name])) {
198
            unset($checks[$et][$entity->id()][$name]);
199
          }
200
        }
201
        if (empty($checks[$et][$entity->id()])) {
202
          unset($checks[$et][$entity->id()]);
203
        }
204
      }
205
      if (empty($checks[$et])) {
206
        unset($checks[$et]);
207
      }
208
    }
209
    return new Result(self::STEP_ER, empty($checks), $checks);
210
  }
211
212
  /**
213
   * Verifies integrity of dynamic_entity_reference forward links.
214
   *
215
   * @return \Drupal\qa\Result
216
   *   The sub-check results.
217
   */
218
  public function checkDynamicEntityReference(): ?Result {
219
    return NULL;
220
  }
221
222
  /**
223
   * Verifies entity_reference_revisions forward and backward links.
224
   *
225
   * @return \Drupal\qa\Result
226
   *   The sub-check results.
227
   */
228
  public function checkEntityReferenceRevisions(): ?Result {
229
    return NULL;
230
  }
231
232
  /**
233
   * {@inheritdoc}
234
   */
235
  public function run(): Pass {
236
    $pass = parent::run();
237
    $pass->record($this->checkEntityReference());
238
    $pass->life->modify();
239
    $pass->record($this->checkDynamicEntityReference());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->checkDynamicEntityReference() targeting Drupal\qa\Plugin\QaCheck...ynamicEntityReference() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
240
    $pass->life->modify();
241
    $pass->record($this->checkEntityReferenceRevisions());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->checkEntityReferenceRevisions() targeting Drupal\qa\Plugin\QaCheck...ityReferenceRevisions() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
242
    $pass->life->end();
243
    return $pass;
244
  }
245
246
}
247