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

EntityReferenceDrupal8::processValue()   C

Complexity

Conditions 7
Paths 17

Size

Total Lines 36
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 17
nc 17
nop 1
1
<?php
2
namespace Drupal\Driver\Plugin\DriverField;
3
4
use Drupal\Driver\Plugin\DriverFieldPluginDrupal8Base;
5
use Drupal\Driver\Wrapper\Entity\DriverEntityDrupal8;
6
use Drupal\Driver\Plugin\DriverEntityPluginManager;
7
8
/**
9
 * A driver field plugin for entity reference fields.
10
 *
11
 * @DriverField(
12
 *   id = "entity_reference",
13
 *   version = 8,
14
 *   fieldTypes = {
15
 *     "entity_reference",
16
 *   },
17
 *   weight = -100,
18
 * )
19
 */
20
class EntityReferenceDrupal8 extends DriverFieldPluginDrupal8Base
21
{
22
23
  /**
24
   * The entity type id.
25
   *
26
   * @var string
27
   */
28
    protected $entity_type_id;
29
30
  /**
31
   * Machine names of the fields or properties to use as labels for targets.
32
   *
33
   * @var array
34
   */
35
    protected $label_keys;
36
37
  /**
38
   * The machine name of the field or property to use as id for targets.
39
   *
40
   * @var string
41
   */
42
    protected $id_key;
43
44
  /**
45
   * The bundles that targets must belong to.
46
   *
47
   * @var string
48
   */
49
    protected $target_bundles;
50
51
  /**
52
   * The machine name of the field that holds the bundle reference for targets.
53
   *
54
   * @var string
55
   */
56
    protected $target_bundle_key;
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->entity_type_id = $this->field->getStorageDefinition()->getSetting('target_type');
70
        $entity_definition = \Drupal::entityManager()->getDefinition($this->entity_type_id);
71
        $this->id_key = $entity_definition->getKey('id');
72
        $this->label_keys = $this->getLabelKeys();
73
74
        // Determine target bundle restrictions.
75
        if ($this->target_bundles = $this->getTargetBundles()) {
76
            $this->target_bundle_key = $entity_definition->getKey('bundle');
77
        }
78
    }
79
80
  /**
81
   * {@inheritdoc}
82
   */
83
    public function processValue($value)
84
    {
85
        if (is_array($value['target_id'])) {
86
            throw new \Exception("Array value not expected: " . print_r($value['target_id'], true));
87
        }
88
89
        // Build a set of strategies for matching target entities with the supplied
90
        // identifier text.
91
        // Id key is useful for matching config entities as they have string ids.
92
        // Id exact match takes precedence over label matches; label matches take
93
        // precedence over id key without underscores matches.
94
        $matchTypes = [];
95
        $matchTypes[] = ['key' => $this->id_key, 'value' => $value['target_id']];
96
        foreach ($this->label_keys as $labelKey) {
97
            $matchTypes[] = ['key' => $labelKey, 'value' => $value['target_id']];
98
        }
99
        $matchTypes[] = ['key' => $this->id_key, 'value' => str_replace(' ', '_', $value['target_id'])];
100
101
        // Try various matching strategies until we find a match.
102
        foreach ($matchTypes as $matchType) {
103
            // Ignore this strategy if the needed key has not been determined.
104
            // D8 key look ups return empty strings if there is no key of that kind.
105
            if (empty($matchType['key'])) {
106
                continue;
107
            }
108
            $targetId = $this->queryByKey($matchType['key'], $matchType['value']);
109
            if (!is_null($targetId)) {
110
                break;
111
            }
112
        }
113
114
        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...
115
            throw new \Exception(sprintf("No entity of type '%s' has id or label matching '%s'.", $this->entity_type_id, $value['target_id']));
116
        }
117
        return ['target_id' => $targetId];
118
    }
119
120
  /**
121
   * Retrieves bundles for which the field is configured to reference.
122
   *
123
   * @return mixed
124
   *   Array of bundle names, or NULL if not able to determine bundles.
125
   */
126
    protected function getTargetBundles()
127
    {
128
        $settings = $this->field->getDefinition()->getSettings();
129
        if (!empty($settings['handler_settings']['target_bundles'])) {
130
            return $settings['handler_settings']['target_bundles'];
131
        }
132
    }
133
134
  /**
135
   * Retrieves fields to try as the label on the entity being referenced.
136
   *
137
   * @return array
138
   *   Array of field machine names.
139
   */
140
    protected function getLabelKeys()
141
    {
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
    {
154
        $projectPluginRoot = $this->field->getProjectPluginRoot();
155
156
        // Build the basic config for the plugin.
157
        $targetEntity = new DriverEntityDrupal8($this->entity_type_id);
158
        $config = [
159
        'type' => $this->entity_type_id,
160
        'projectPluginRoot' => $projectPluginRoot,
161
        ];
162
163
        // Get a bundle specific plugin only if the entity reference field is
164
        // targeting a single bundle.
165
        if (is_array($this->target_bundles) && count($this->target_bundles) === 1) {
166
            $config['bundle'] = $this->target_bundles[0];
167
            $targetEntity->setBundle($this->target_bundles[0]);
168
        } else {
169
            $config['bundle'] = $this->entity_type_id;
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 integer|string
197
   *   The id of an entity that has $value in the $key field.
198
   */
199
    protected function queryByKey($key, $value)
200
    {
201
        $query = \Drupal::entityQuery($this->entity_type_id);
202
        // @todo make this always case-insensitive.
203
        $query->condition($key, $value);
204
        if ($this->target_bundles && $this->target_bundle_key) {
205
            $query->condition($this->target_bundle_key, $this->target_bundles, 'IN');
206
        }
207
        $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...
208
        if ($entities = $query->execute()) {
209
            $target_id = array_shift($entities);
210
            return $target_id;
211
        }
212
    }
213
}
214