Completed
Pull Request — 1.0.x (#20)
by Fabian
01:46
created

Drupal8::getTaxonomyTermId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace NuvoleWeb\Drupal\Driver\Cores;
4
5
use function bovigo\assert\assert;
6
use function bovigo\assert\predicate\hasKey;
7
use function bovigo\assert\predicate\isNotEmpty;
8
use function bovigo\assert\predicate\isNotEqualTo;
9
use Drupal\Core\Cache\Cache;
10
use Drupal\Driver\Cores\Drupal8 as OriginalDrupal8;
11
use Drupal\menu_link_content\Entity\MenuLinkContent;
12
use Drupal\node\Entity\Node;
13
use Drupal\node\Entity\NodeType;
14
use Drupal\system\Entity\Menu;
15
use Drupal\taxonomy\Entity\Term;
16
use Drupal\taxonomy\Entity\Vocabulary;
17
use NuvoleWeb\Drupal\Driver\Objects\Drupal8\EditableConfig;
18
use NuvoleWeb\Drupal\Driver\Objects\Drupal8\State;
19
20
/**
21
 * Class Drupal8.
22
 *
23
 * @package NuvoleWeb\Drupal\Driver\Cores
24
 */
25
class Drupal8 extends OriginalDrupal8 implements CoreInterface {
26
27
  /**
28
   * {@inheritdoc}
29
   */
30 View Code Duplication
  public function convertLabelToNodeTypeId($type) {
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...
31
    // First suppose that the id has been passed.
32
    if (NodeType::load($type)) {
33
      return $type;
34
    }
35
    $storage = \Drupal::entityTypeManager()->getStorage('node_type');
36
    $result = $storage->loadByProperties(['name' => $type]);
37
    assert($result, isNotEmpty());
38
    return key($result);
39
  }
40
41
  /**
42
   * {@inheritdoc}
43
   */
44 View Code Duplication
  public function convertLabelToTermTypeId($type) {
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...
45
    // First suppose that the id has been passed.
46
    if (Vocabulary::load($type)) {
47
      return $type;
48
    }
49
    $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary');
50
    $result = $storage->loadByProperties(['name' => $type]);
51
    assert($result, isNotEmpty());
52
    return key($result);
53
  }
54
55
  /**
56
   * {@inheritdoc}
57
   */
58 View Code Duplication
  public function loadNodeByName($title) {
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...
59
    $result = \Drupal::entityQuery('node')
60
      ->condition('title', $title)
61
      ->condition('status', NODE_PUBLISHED)
62
      ->range(0, 1)
63
      ->execute();
64
    assert($result, isNotEmpty());
65
    $nid = current($result);
66
    return Node::load($nid);
67
  }
68
69
  /**
70
   * {@inheritdoc}
71
   */
72
  public function getEntityIdByLabel($entity_type, $bundle, $label) {
73
    /** @var \Drupal\node\NodeStorage $storage */
74
    $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
75
    $bundle_key = $storage->getEntityType()->getKey('bundle');
76
    $label_key = $storage->getEntityType()->getKey('label');
77
78
    $query = \Drupal::entityQuery($entity_type);
79
    if ($bundle) {
80
      $query->condition($bundle_key, $bundle);
81
    }
82
    $query->condition($label_key, $label);
83
    $query->range(0, 1);
84
85
    $result = $query->execute();
86
    assert($result, isNotEmpty(), __METHOD__ . ": No Entity {$entity_type} with name {$label} found.");
87
    return current($result);
88
  }
89
90
  /**
91
   * {@inheritdoc}
92
   */
93
  public function loadUserByName($name) {
94
    $user = user_load_by_name($name);
95
    assert($user, isNotEqualTo(FALSE));
96
    return $user;
97
  }
98
99
  /**
100
   * {@inheritdoc}
101
   */
102
  public function nodeAccess($op, $name, $node) {
103
    $account = $this->loadUserByName($name);
104
    return $node->access($op, $account);
105
  }
106
107
  /**
108
   * {@inheritdoc}
109
   */
110
  public function getNodeId($node) {
111
    return $node->id();
112
  }
113
114
  /**
115
   * {@inheritdoc}
116
   */
117 View Code Duplication
  public function loadTaxonomyTermByName($type, $name) {
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...
118
    $result = \Drupal::entityQuery('taxonomy_term')
119
      ->condition('name', $name)
120
      ->condition('vid', $type)
121
      ->range(0, 1)
122
      ->execute();
123
    assert($result, isNotEmpty());
124
    $id = current($result);
125
    return Term::load($id);
126
  }
127
128
  /**
129
   * {@inheritdoc}
130
   */
131
  public function getTaxonomyTermId($term) {
132
    return $term->id();
133
  }
134
135
  /**
136
   * {@inheritdoc}
137
   */
138
  public function loadMenuItemByTitle($menu_name, $title) {
139
    $items = \Drupal::entityTypeManager()->getStorage('menu_link_content')
140
      ->loadByProperties([
141
        'menu_name' => $menu_name,
142
        'title' => $title,
143
      ]);
144
    if (!empty($items)) {
145
      return array_shift($items);
146
    }
147
    else {
148
      return NULL;
149
    }
150
  }
151
152
  /**
153
   * {@inheritdoc}
154
   */
155
  public function createMenuStructure($menu_name, $menu_items) {
156
    if (!Menu::load($menu_name)) {
157
      throw new \InvalidArgumentException("Menu '{$menu_name}' not found.");
158
    }
159
160
    $weight = 0;
161
    $menu_links = [];
162
    foreach ($menu_items as $menu_item) {
163
      $values = [
164
        'title' => $menu_item['title'],
165
        'link' => ['uri' => $menu_item['uri']],
166
        'menu_name' => $menu_name,
167
        'weight' => $weight++,
168
      ];
169
170
      // Assign parent item.
171
      if ($menu_item['parent']) {
172
        $values['parent'] = $menu_item['parent'];
173
        $parent = $this->loadMenuItemByTitle($menu_name, $menu_item['parent']);
174
        if ($parent) {
175
          $values['parent'] = $parent->getPluginId();
176
        }
177
      }
178
179
      // Create menu link.
180
      $menu_link = MenuLinkContent::create($values);
181
      $menu_link->save();
182
      $menu_links[] = $menu_link;
183
    }
184
185
    return $menu_links;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $menu_links; (array) is incompatible with the return type declared by the interface NuvoleWeb\Drupal\Driver\...ce::createMenuStructure of type object.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
186
  }
187
188
  /**
189
   * {@inheritdoc}
190
   */
191
  public function clearMenuCache() {
192
    \Drupal::cache('menu')->invalidateAll();
193
  }
194
195
  /**
196
   * {@inheritdoc}
197
   */
198
  public function invalidateCacheTags(array $tags) {
199
    Cache::invalidateTags($tags);
200
  }
201
202
  /**
203
   * Create an entity.
204
   *
205
   * @param string $entity_type
206
   *   Entity type.
207
   * @param array $values
208
   *   The Values to create the entity with.
209
   * @param bool $save
210
   *   Indicate whether to directly save the entity or not.
211
   *
212
   * @return EntityInterface
213
   *   Entity object.
214
   */
215
  public function entityCreate($entity_type, $values, $save = TRUE) {
216
    if (!is_array($values)) {
217
      // Cast an object to array to be compatible with nodeCreate().
218
      $values = (array) $values;
219
    }
220
221
    $entity = $this->getStubEntity($entity_type, $values);
222
223
    foreach ($values as $name => $value) {
224
      $definition = $this->getFieldDefinition($entity->getEntityTypeId(), $name);
225
      $settings = $definition->getSettings();
226
      switch ($definition->getType()) {
227
        case 'entity_reference':
228
          if (in_array($settings['target_type'], ['node', 'taxonomy_term'])) {
229
            // @todo: only supports single values for the moment.
230
            $id = $this->getEntityIdByLabel($settings['target_type'], NULL, $value);
231
            $entity->{$name}->setValue($id);
232
          }
233
          break;
234
235
        case 'entity_reference_revisions':
236
          $entities = [];
237
          foreach ($value as $target_values) {
238
            assert($target_values, hasKey('type'), __METHOD__ . ": Required fields 'type' not found.");
239
            $entities[] = $this->entityCreate($settings['target_type'], $target_values, FALSE);
240
          }
241
242
          $entity->{$name}->setValue($entities);
243
          break;
244
      }
245
    }
246
247
    if ($save) {
248
      $entity->save();
249
    }
250
251
    return $entity;
252
  }
253
254
  /**
255
   * {@inheritdoc}
256
   */
257
  public function entityLoad($entity_type, $entity_id) {
258
    return \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
259
  }
260
261
  /**
262
   * {@inheritdoc}
263
   */
264
  public function entityDelete($entity) {
265
    $entity->delete();
266
  }
267
268
  /**
269
   * {@inheritdoc}
270
   */
271
  public function entityAddTranslation($entity, $language, array $values) {
272
    /** @var ContentEntityInterface $translation */
273
    $translation = $this->getStubEntity($entity->getEntityTypeId(), $values);
274
275
    foreach ($values as $name => $value) {
276
      $definition = $this->getFieldDefinition($translation->getEntityTypeId(), $name);
277
      $settings = $definition->getSettings();
278
      $source_values = $entity->get($name)->getValue();
279
      switch ($definition->getType()) {
280
        case 'entity_reference':
281
          if (in_array($settings['target_type'], ['node', 'taxonomy_term'])) {
282
            // @todo: only supports single values for the moment.
283
            $translation->{$name}->setValue($source_values);
284
          }
285
          break;
286
287
        case 'entity_reference_revisions':
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
288
289
          // When reference field is translatable then we will need to create
290
          // new entities and reference them.
291
          // @link https://www.drupal.org/node/2461695
292
          if ($definition->isTranslatable()) {
293
            $target_values = [];
294
            foreach ($source_values as $key => $item) {
295
              $_entity = $this->entityCreate($settings['target_type'], $value[$key]);
296
              $target_values[] = [
297
                'target_id' => $_entity->id(),
298
                'target_revision_id' => $_entity->getRevisionId(),
299
              ];
300
            }
301
            $translation->{$name}->setValue($target_values);
302
          }
303
          else {
304
            // Recurse over the referenced entities.
305
            $source = $this->entityLoad($settings['target_type'], $item['target_id']);
0 ignored issues
show
Bug introduced by
The variable $item seems to be defined by a foreach iteration on line 294. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
306
            $this->entityAddTranslation($source, $language, $value[$key]);
0 ignored issues
show
Bug introduced by
The variable $key seems to be defined by a foreach iteration on line 294. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
307
          }
308
          break;
309
      }
310
    }
311
312
    // Add the translation to the entity.
313
    $translation = $entity->addTranslation($language, $translation->toArray());
314
315
    $translation->save();
316
317
    return $translation;
318
  }
319
320
  /**
321
   * {@inheritdoc}
322
   */
323
  public function state() {
324
    return new State();
325
  }
326
327
  /**
328
   * {@inheritdoc}
329
   */
330
  public function getEditableConfig($name) {
331
    return new EditableConfig($name);
332
  }
333
334
  /**
335
   * Get field definition.
336
   *
337
   * @param string $entity_type
338
   *    Entity type machine name.
339
   * @param string $field_name
340
   *    Field machine name.
341
   *
342
   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
343
   *    Field definition.
344
   */
345
  protected function getFieldDefinition($entity_type, $field_name) {
346
    $definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type);
347
    assert($definitions, hasKey($field_name), __METHOD__ . ": Field '{$field_name}' not found for entity type '{$entity_type}'.");
348
    return $definitions[$field_name];
349
  }
350
351
  /**
352
   * Get stub entity.
353
   *
354
   * @param string $entity_type
355
   *    Entity type.
356
   * @param array $values
357
   *    Entity values.
358
   *
359
   * @return \Drupal\Core\Entity\EntityInterface
360
   *    Entity object.
361
   */
362
  protected function getStubEntity($entity_type, array $values) {
363
    return \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
364
  }
365
366
}
367