Completed
Pull Request — master (#157)
by
unknown
01:57
created

EntityReferenceDrupal8::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 11
nc 2
nop 3
1
<?php
2
3
namespace Drupal\Driver\Plugin\DriverField;
4
5
use Drupal\Driver\Plugin\DriverFieldPluginDrupal8Base;
6
use Drupal\Driver\Wrapper\Entity\DriverEntityDrupal8;
7
use Drupal\Driver\Plugin\DriverEntityPluginManager;
8
9
/**
10
 * A driver field plugin for entity reference fields.
11
 *
12
 * @DriverField(
13
 *   id = "entity_reference",
14
 *   version = 8,
15
 *   fieldTypes = {
16
 *     "entity_reference",
17
 *   },
18
 *   weight = -100,
19
 * )
20
 */
21
class EntityReferenceDrupal8 extends DriverFieldPluginDrupal8Base {
22
23
  /**
24
   * The entity type id.
25
   *
26
   * @var string
27
   */
28
  protected $entityTypeId;
29
30
  /**
31
   * Machine names of the fields or properties to use as labels for targets.
32
   *
33
   * @var array
34
   */
35
  protected $labelKeys;
36
37
  /**
38
   * The machine name of the field or property to use as id for targets.
39
   *
40
   * @var string
41
   */
42
  protected $idKey;
43
44
  /**
45
   * The bundles that targets must belong to.
46
   *
47
   * @var string
48
   */
49
  protected $targetBundles;
50
51
  /**
52
   * The machine name of the field that holds the bundle reference for targets.
53
   *
54
   * @var string
55
   */
56
  protected $targetBundleKey;
57
58
  /**
59
   * {@inheritdoc}
60
   */
61
  public function __construct(
62
        array $configuration,
63
        $plugin_id,
64
        $plugin_definition
65
    ) {
66
    parent::__construct($configuration, $plugin_id, $plugin_definition);
67
68
    // Determine id & label keys.
69
    $this->entityTypeId = $this->field->getStorageDefinition()->getSetting('target_type');
70
    $entity_definition = \Drupal::entityManager()->getDefinition($this->entityTypeId);
71
    $this->idKey = $entity_definition->getKey('id');
72
    $this->labelKeys = $this->getLabelKeys();
73
74
    // Determine target bundle restrictions.
75
    if ($this->targetBundles = $this->getTargetBundles()) {
76
      $this->targetBundleKey = $entity_definition->getKey('bundle');
77
    }
78
  }
79
80
  /**
81
   * {@inheritdoc}
82
   */
83
  public function processValue($value) {
84
    if (is_array($value['target_id'])) {
85
      throw new \Exception("Array value not expected: " . print_r($value['target_id'], TRUE));
86
    }
87
88
    // Build a set of strategies for matching target entities with the supplied
89
    // identifier text.
90
    // Id key is useful for matching config entities as they have string ids.
91
    // Id exact match takes precedence over label matches; label matches take
92
    // precedence over id key without underscores matches.
93
    $matchTypes = [];
94
    $matchTypes[] = ['key' => $this->idKey, 'value' => $value['target_id']];
95
    foreach ($this->labelKeys as $labelKey) {
96
      $matchTypes[] = ['key' => $labelKey, 'value' => $value['target_id']];
97
    }
98
    $matchTypes[] = [
99
      'key' => $this->idKey,
100
      'value' => str_replace(' ', '_', $value['target_id']),
101
    ];
102
103
    // Try various matching strategies until we find a match.
104
    foreach ($matchTypes as $matchType) {
105
      // Ignore this strategy if the needed key has not been determined.
106
      // D8 key look ups return empty strings if there is no key of that kind.
107
      if (empty($matchType['key'])) {
108
        continue;
109
      }
110
      $targetId = $this->queryByKey($matchType['key'], $matchType['value']);
111
      if (!is_null($targetId)) {
112
        break;
113
      }
114
    }
115
116
    if (is_null($targetId)) {
0 ignored issues
show
Bug introduced by
The variable $targetId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
117
      throw new \Exception(sprintf("No entity of type '%s' has id or label matching '%s'.", $this->entityTypeId, $value['target_id']));
118
    }
119
    return ['target_id' => $targetId];
120
  }
121
122
  /**
123
   * Retrieves bundles for which the field is configured to reference.
124
   *
125
   * @return mixed
126
   *   Array of bundle names, or NULL if not able to determine bundles.
127
   */
128
  protected function getTargetBundles() {
129
    $settings = $this->field->getDefinition()->getSettings();
130
    if (!empty($settings['handler_settings']['target_bundles'])) {
131
      return $settings['handler_settings']['target_bundles'];
132
    }
133
  }
134
135
  /**
136
   * Retrieves fields to try as the label on the entity being referenced.
137
   *
138
   * @return array
139
   *   Array of field machine names.
140
   */
141
  protected function getLabelKeys() {
142
    $plugin = $this->getEntityPlugin();
143
    return $plugin->getLabelKeys();
144
  }
145
146
  /**
147
   * Get an entity plugin for the entity reference target entity type.
148
   *
149
   * @return \Drupal\Driver\Plugin\DriverEntityPluginInterface
150
   *   An instantiated driver entity plugin object.
151
   */
152
  protected function getEntityPlugin() {
153
    $projectPluginRoot = $this->field->getProjectPluginRoot();
154
155
    // Build the basic config for the plugin.
156
    $targetEntity = new DriverEntityDrupal8($this->entityTypeId);
157
    $config = [
158
      'type' => $this->entityTypeId,
159
      'projectPluginRoot' => $projectPluginRoot,
160
    ];
161
162
    // Get a bundle specific plugin only if the entity reference field is
163
    // targeting a single bundle.
164
    if (is_array($this->targetBundles) && count($this->targetBundles) === 1) {
165
      $config['bundle'] = $this->targetBundles[0];
166
      $targetEntity->setBundle($this->targetBundles[0]);
167
    }
168
    else {
169
      $config['bundle'] = $this->entityTypeId;
170
    }
171
172
    // Discover & instantiate plugin.
173
    $namespaces = \Drupal::service('container.namespaces');
174
    $cache_backend = $cache_backend = \Drupal::service('cache.discovery');
175
    $module_handler = $module_handler = \Drupal::service('module_handler');
176
    $manager = new DriverEntityPluginManager($namespaces, $cache_backend, $module_handler, $this->pluginDefinition['version'], $this->field->getProjectPluginRoot());
177
178
    // Get only the highest priority matched plugin.
179
    $matchedDefinitions = $manager->getMatchedDefinitions($targetEntity);
180
    if (count($matchedDefinitions) === 0) {
181
      throw new \Exception("No matching DriverEntity plugins found.");
182
    }
183
    $topDefinition = $matchedDefinitions[0];
184
    $plugin = $manager->createInstance($topDefinition['id'], $config);
185
    return $plugin;
186
  }
187
188
  /**
189
   * Find an entity by looking at id and labels keys.
190
   *
191
   * @param string $key
192
   *   The machine name of the field to query.
193
   * @param string $value
194
   *   The value to seek in the field.
195
   *
196
   * @return int|string
197
   *   The id of an entity that has $value in the $key field.
198
   */
199
  protected function queryByKey($key, $value) {
200
    $query = \Drupal::entityQuery($this->entityTypeId);
201
    // @todo make this always case-insensitive.
202
    $query->condition($key, $value);
203
    if ($this->targetBundles && $this->targetBundleKey) {
204
      $query->condition($this->targetBundleKey, $this->targetBundles, 'IN');
205
    }
206
    $entities = $query->execute();
0 ignored issues
show
Unused Code introduced by
$entities is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
207
    if ($entities = $query->execute()) {
208
      $target_id = array_shift($entities);
209
      return $target_id;
210
    }
211
  }
212
213
}
214