Completed
Push — 1.0.x ( b718a3...04e9a6 )
by Fabian
02:26
created

Drupal8::entityAddTranslation()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 35
Code Lines 20

Duplication

Lines 7
Ratio 20 %

Importance

Changes 0
Metric Value
dl 7
loc 35
c 0
b 0
f 0
rs 8.439
cc 6
eloc 20
nc 6
nop 3
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\Entity\ContentEntityInterface;
10
use Drupal\Core\Entity\EntityInterface;
11
use Drupal\Driver\Cores\Drupal8 as OriginalDrupal8;
12
use Drupal\menu_link_content\Entity\MenuLinkContent;
13
use Drupal\node\Entity\Node;
14
use Drupal\node\Entity\NodeType;
15
use Drupal\system\Entity\Menu;
16
use Drupal\taxonomy\Entity\Term;
17
use Drupal\taxonomy\Entity\Vocabulary;
18
19
/**
20
 * Class Drupal8.
21
 *
22
 * @package NuvoleWeb\Drupal\Driver\Cores
23
 */
24
class Drupal8 extends OriginalDrupal8 implements CoreInterface {
25
26
  /**
27
   * {@inheritdoc}
28
   */
29 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...
30
    // First suppose that the id has been passed.
31
    if (NodeType::load($type)) {
32
      return $type;
33
    }
34
    $storage = \Drupal::entityTypeManager()->getStorage('node_type');
35
    $result = $storage->loadByProperties(['name' => $type]);
36
    assert($result, isNotEmpty());
37
    return key($result);
38
  }
39
40
  /**
41
   * {@inheritdoc}
42
   */
43 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...
44
    // First suppose that the id has been passed.
45
    if (Vocabulary::load($type)) {
46
      return $type;
47
    }
48
    $storage = \Drupal::entityTypeManager()->getStorage('taxonomy_vocabulary');
49
    $result = $storage->loadByProperties(['name' => $type]);
50
    assert($result, isNotEmpty());
51
    return key($result);
52
  }
53
54
  /**
55
   * {@inheritdoc}
56
   */
57 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...
58
    $result = \Drupal::entityQuery('node')
59
      ->condition('title', $title)
60
      ->condition('status', NODE_PUBLISHED)
61
      ->range(0, 1)
62
      ->execute();
63
    assert($result, isNotEmpty());
64
    $nid = current($result);
65
    return Node::load($nid);
66
  }
67
68
  /**
69
   * {@inheritdoc}
70
   */
71
  public function getEntityIdByLabel($entity_type, $bundle, $label) {
72
    /** @var \Drupal\node\NodeStorage $storage */
73
    $storage = \Drupal::entityTypeManager()->getStorage($entity_type);
74
    $bundle_key = $storage->getEntityType()->getKey('bundle');
75
    $label_key = $storage->getEntityType()->getKey('label');
76
77
    $query = \Drupal::entityQuery($entity_type);
78
    if ($bundle) {
79
      $query->condition($bundle_key, $bundle);
80
    }
81
    $query->condition($label_key, $label);
82
    $query->range(0, 1);
83
84
    $result = $query->execute();
85
    assert($result, isNotEmpty(), __METHOD__ . ": No Entity {$entity_type} with name {$label} found.");
86
    return current($result);
87
  }
88
89
  /**
90
   * {@inheritdoc}
91
   */
92
  public function loadUserByName($name) {
93
    $user = user_load_by_name($name);
94
    assert($user, isNotEqualTo(FALSE));
95
    return $user;
96
  }
97
98
  /**
99
   * {@inheritdoc}
100
   */
101
  public function nodeAccess($op, $name, $node) {
102
    $account = $this->loadUserByName($name);
103
    return $node->access($op, $account);
104
  }
105
106
  /**
107
   * {@inheritdoc}
108
   */
109
  public function getNodeId($node) {
110
    return $node->id();
111
  }
112
113
  /**
114
   * {@inheritdoc}
115
   */
116 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...
117
    $result = \Drupal::entityQuery('taxonomy_term')
118
      ->condition('name', $name)
119
      ->condition('vid', $type)
120
      ->range(0, 1)
121
      ->execute();
122
    assert($result, isNotEmpty());
123
    $id = current($result);
124
    return Term::load($id);
125
  }
126
127
  /**
128
   * {@inheritdoc}
129
   */
130
  public function getTaxonomyTermId($term) {
131
    return $term->id();
132
  }
133
134
  /**
135
   * {@inheritdoc}
136
   */
137
  public function loadMenuItemByTitle($menu_name, $title) {
138
    $items = \Drupal::entityTypeManager()->getStorage('menu_link_content')
139
      ->loadByProperties([
140
        'menu_name' => $menu_name,
141
        'title' => $title,
142
      ]);
143
    if (!empty($items)) {
144
      return array_shift($items);
145
    }
146
    else {
147
      return NULL;
148
    }
149
  }
150
151
  /**
152
   * {@inheritdoc}
153
   */
154
  public function createMenuStructure($menu_name, $menu_items) {
155
    if (!Menu::load($menu_name)) {
156
      throw new \InvalidArgumentException("Menu '{$menu_name}' not found.");
157
    }
158
159
    /* @var \Drupal\menu_link_content\Plugin\Menu\MenuLinkContent[] $parents */
160
    $parents = [];
161
    $weight = 0;
162
    $menu_links = [];
163
    foreach ($menu_items as $menu_item) {
164
      $values = [
165
        'title' => $menu_item['title'],
166
        'link' => ['uri' => $menu_item['uri']],
167
        'menu_name' => $menu_name,
168
        'weight' => $weight++,
169
      ];
170
171
      // Assign parent item.
172
      if ($menu_item['parent']) {
173
        $values['parent'] = $menu_item['parent'];
174
        $parent = $this->loadMenuItemByTitle($menu_name, $menu_item['parent']);
175
        if ($parent) {
176
          $values['parent'] = $parent->getPluginId();
177
        }
178
      }
179
180
      // Create menu link.
181
      $menu_link = MenuLinkContent::create($values);
182
      $menu_link->save();
183
184
      // Store current menu link in parents array for later use in loop.
185
      $parents[$menu_item['title']] = $menu_link;
186
      $menu_links[] = $menu_link;
187
    }
188
189
    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...
190
  }
191
192
  /**
193
   * {@inheritdoc}
194
   */
195
  public function clearMenuCache() {
196
    \Drupal::cache('menu')->invalidateAll();
197
  }
198
199
  /**
200
   * Create an entity.
201
   *
202
   * @param string $entity_type
203
   *   Entity type.
204
   * @param array $values
205
   *   The Values to create the entity with.
206
   * @param boolean $save
207
   *   Indicate
208
   *
209
   * @return EntityInterface
210
   *   Entity object.
211
   */
212
  public function entityCreate($entity_type, $values, $save = TRUE) {
213
    if (!is_array($values)) {
214
      // Cast an object to array to be compatible with nodeCreate().
215
      $values = (array) $values;
216
    }
217
218
    $entity = $this->getStubEntity($entity_type, $values);
219
220
    foreach ($values as $name => $value) {
221
      $definition = $this->getFieldDefinition($entity->getEntityTypeId(), $name);
222
      $settings = $definition->getSettings();
223
      switch ($definition->getType()) {
224 View Code Duplication
        case 'entity_reference':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
225
          if (in_array($settings['target_type'], ['node', 'taxonomy_term'])) {
226
            // @todo: only supports single values for the moment.
227
            $id = $this->getEntityIdByLabel($settings['target_type'], NULL, $value);
228
            $entity->{$name}->setValue($id);
229
          }
230
          break;
231
232
        case 'entity_reference_revisions':
233
          $entities = [];
234
          foreach ($value as $target_values) {
235
            assert($target_values, hasKey('type'), __METHOD__ . ": Required fields 'type' not found.");
236
            $entities[] = $this->entityCreate($settings['target_type'], $target_values, FALSE);
237
          }
238
239
          $entity->{$name}->setValue($entities);
240
          break;
241
      }
242
    }
243
244
    if ($save) {
245
      $entity->save();
246
    }
247
248
    return $entity;
249
  }
250
251
  /**
252
   * {@inheritdoc}
253
   */
254
  public function entityLoad($entity_type, $entity_id) {
255
    return \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
256
  }
257
258
  /**
259
   * @param EntityInterface $entity
260
   */
261
  public function entityDelete($entity) {
262
    $entity->delete();
263
  }
264
265
  /**
266
   * Add a translation for an entity.
267
   *
268
   * @param ContentEntityInterface $entity
269
   *   The entity to translate.
270
   * @param string $language
271
   *   The language to translate to.
272
   * @param array $values
273
   *   The values for the translation.
274
   *
275
   * @return object
276
   *   The translation entity.
277
   */
278
  public function entityAddTranslation($entity, $language, array $values) {
279
280
    /** @var ContentEntityInterface $translation */
281
    $translation = $this->getStubEntity($entity->getEntityTypeId(), $values);
282
283
    foreach ($values as $name => $value) {
284
      $definition = $this->getFieldDefinition($translation->getEntityTypeId(), $name);
285
      $settings = $definition->getSettings();
286
      switch ($definition->getType()) {
287 View Code Duplication
        case 'entity_reference':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
288
          if (in_array($settings['target_type'], ['node', 'taxonomy_term'])) {
289
            // @todo: only supports single values for the moment.
290
            $source_values = $entity->get($name)->getValue();
291
            $translation->{$name}->setValue($source_values);
292
          }
293
          break;
294
295
        case 'entity_reference_revisions':
296
          $source_values = $entity->get($name)->getValue();
297
          foreach ($source_values as $key => $item) {
298
            // Recurse over the referenced entities.
299
            $source = $this->entityLoad($settings['target_type'], $item['target_id']);
300
            $this->entityAddTranslation($source, $language, $value[$key]);
301
          }
302
          break;
303
      }
304
    }
305
306
    // Add the translation to the entity.
307
    $translation = $entity->addTranslation($language, $translation->toArray());
308
309
    $translation->save();
310
311
    return $translation;
312
  }
313
314
  /**
315
   * Get field definition.
316
   *
317
   * @param string $entity_type
318
   *    Entity type machine name.
319
   * @param string $field_name
320
   *    Field machine name.
321
   *
322
   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
323
   *    Field definition.
324
   */
325
  protected function getFieldDefinition($entity_type, $field_name) {
326
    $definitions = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type);
327
    assert($definitions, hasKey($field_name), __METHOD__ . ": Field '{$field_name}' not found for entity type '{$entity_type}'.");
328
    return $definitions[$field_name];
329
  }
330
331
332
  /**
333
   * Get stub entity.
334
   *
335
   * @param string $entity_type
336
   *    Entity type.
337
   * @param array $values
338
   *    Entity values.
339
   *
340
   * @return \Drupal\Core\Entity\EntityInterface
341
   *    Entity object.
342
   */
343
  protected function getStubEntity($entity_type, array $values) {
344
    return \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
345
  }
346
347
}
348