Completed
Push — 8.x-1.x ( 657e32...c248cd )
by Janez
02:38
created

EntityHelperTrait::buildEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 4
c 1
b 1
f 0
nc 1
nop 3
dl 0
loc 5
rs 9.4285
1
<?php
2
3
namespace Drupal\entity_embed;
4
5
use Drupal\Component\Utility\Html;
6
use Drupal\Core\Entity\EntityInterface;
7
use Drupal\Core\Entity\EntityManagerInterface;
8
use Drupal\Core\Entity\EntityStorageException;
9
use Drupal\Core\Extension\ModuleHandlerInterface;
10
use Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager;
11
12
/**
13
 * Wrapper methods for entity loading and rendering.
14
 *
15
 * This utility trait should only be used in application-level code, such as
16
 * classes that would implement ContainerInjectionInterface. Services registered
17
 * in the Container should not use this trait but inject the appropriate service
18
 * directly for easier testing.
19
 *
20
 * @todo this duplicates/wraps much of the Entity API. Is this really worth
21
 * keeping? The downside is painfully illustrated: the documentation must be
22
 * kept up to date with the actual API documentation… and that's not happening.
23
 * This causes subtle bugs and makes maintenance harder.
24
 */
25
trait EntityHelperTrait {
26
27
  /**
28
   * The entity manager service.
29
   *
30
   * @var \Drupal\Core\Entity\EntityManagerInterface
31
   */
32
  protected $entityManager;
33
34
  /**
35
   * The module handler service.
36
   *
37
   * @var \Drupal\Core\Extension\ModuleHandlerInterface.
38
   */
39
  protected $moduleHandler;
40
41
  /**
42
   * The Entity Embed Display plugin manager.
43
   *
44
   * @var \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager.
45
   */
46
  protected $displayPluginManager;
47
48
  /**
49
   * Loads an entity from the database.
50
   *
51
   * @param string $entity_type
52
   *   The entity type to load, e.g. node or user.
53
   * @param mixed $id
54
   *   The id or UUID of the entity to load.
55
   *
56
   * @return \Drupal\Core\Entity\EntityInterface
57
   *   The entity object, or NULL if there is no entity with the given id or
58
   *   UUID.
59
   */
60
  protected function loadEntity($entity_type, $id) {
61
    $entities = $this->loadMultipleEntities($entity_type, array($id));
62
    return !empty($entities) ? reset($entities) : NULL;
63
  }
64
65
  /**
66
   * Loads multiple entities from the database.
67
   *
68
   * @param string $entity_type
69
   *   The entity type to load, e.g. node or user.
70
   * @param array $ids
71
   *   An array of entity IDs or UUIDs.
72
   *
73
   * @return array
74
   *   An array of entity objects indexed by their ids.
75
   *
76
   * @throws \Drupal\Core\Entity\EntityStorageException
77
   *   Throws an exception if the entity type does not supports UUIDs.
78
   */
79
  protected function loadMultipleEntities($entity_type, array $ids) {
80
    $entities = array();
81
    $storage = $this->entityManager()->getStorage($entity_type);
82
83
    $uuids = array_filter($ids, 'Drupal\Component\Uuid\Uuid::isValid');
84
    if (!empty($uuids)) {
85
      $definition = $this->entityManager()->getDefinition($entity_type);
86
      if (!$uuid_key = $definition->getKey('uuid')) {
87
        throw new EntityStorageException("Entity type $entity_type does not support UUIDs.");
88
      }
89
      $entities += $storage->loadByProperties(array($uuid_key => $uuids));
90
    }
91
92
    if ($remaining_ids = array_diff($ids, $uuids)) {
93
      $entities += $storage->loadMultiple($remaining_ids);
94
    }
95
96
    return $entities;
97
  }
98
99
  /**
100
   * Determines if an entity can be rendered.
101
   *
102
   * @param \Drupal\Core\Entity\EntityInterface $entity
103
   *   The entity object.
104
   *
105
   * @return bool
106
   *   TRUE if the entity's type has a view builder controller, otherwise FALSE.
107
   */
108
  protected function canRenderEntity(EntityInterface $entity) {
109
    $entity_type = $entity->getEntityTypeId();
110
    return $this->canRenderEntityType($entity_type);
111
  }
112
113
  /**
114
   * Determines if an entity type can be rendered.
115
   *
116
   * @param string $entity_type
117
   *   The entity type id.
118
   *
119
   * @return bool
120
   *   TRUE if the entitys type has a view builder controller, otherwise FALSE.
121
   */
122
  protected function canRenderEntityType($entity_type) {
123
    return $this->entityManager()->hasHandler($entity_type, 'view_builder');
124
  }
125
126
  /**
127
   * Builds the render array for the provided entity.
128
   *
129
   * @param \Drupal\Core\Entity\EntityInterface $entity
130
   *   The entity to be rendered.
131
   * @param string $view_mode
132
   *   The view mode that should be used to display the entity.
133
   * @param string $langcode
134
   *   (optional) For which language the entity should be rendered, defaults to
135
   *   the current content language.
136
   *
137
   * @return array
138
   *   A render array for the entity.
139
   *
140
   * @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
141
   *
142
   * @todo Note that the signature here does NOT match that of \Drupal\Core\Entity\EntityViewBuilderInterface::view(), which can lead to subtle bugs.
143
   */
144
  protected function buildEntity(EntityInterface $entity, $view_mode, $langcode = NULL) {
145
    return $this->entityManager()
146
      ->getViewBuilder($entity->getEntityTypeId())
147
      ->view($entity, $view_mode, $langcode);
148
  }
149
150
  /**
151
   * Builds the render array for an embedded entity.
152
   *
153
   * @param \Drupal\Core\Entity\EntityInterface $entity
154
   *   The entity to be rendered.
155
   * @param array $context
156
   *   (optional) Array of context values, corresponding to the attributes on
157
   *   the embed HTML tag.
158
   *
159
   * @return array
160
   *   A render array.
161
   *
162
   * @todo improve documentation
163
   */
164
  protected function buildEntityEmbed(EntityInterface $entity, array $context = array()) {
165
    // Support the deprecated view-mode data attribute.
166
    if (isset($context['data-view-mode']) && !isset($context['data-entity-embed-display']) && !isset($context['data-entity-embed-settings'])) {
167
      $context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
168
      $context['data-entity-embed-settings'] = ['view_mode' => &$context['data-view-mode']];
169
    }
170
171
    // Merge in default attributes.
172
    $context += array(
173
      'data-entity-type' => $entity->getEntityTypeId(),
174
      'data-entity-uuid' => $entity->uuid(),
175
      'data-entity-embed-display' => 'entity_reference:entity_reference_entity_view',
176
      'data-entity-embed-settings' => array(),
177
    );
178
179
    // The default Entity Embed Display plugin has been deprecated by the
180
    // rendered entity field formatter.
181
    if ($context['data-entity-embed-display'] === 'default') {
182
      $context['data-entity-embed-display'] = 'entity_reference:entity_reference_entity_view';
183
    }
184
185
    // The caption text is double-encoded, so decode it here.
186
    if (isset($context['data-caption'])) {
187
      $context['data-caption'] = Html::decodeEntities($context['data-caption']);
188
    }
189
190
    // Allow modules to alter the entity prior to embed rendering.
191
    $this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed_context", 'entity_embed_context'), $context, $entity);
192
193
    // Build and render the Entity Embed Display plugin, allowing modules to
194
    // alter the result before rendering.
195
    $build = array(
196
      '#theme_wrappers' => ['entity_embed_container'],
197
      '#attributes' => ['class' => ['embedded-entity']],
198
      '#entity' => $entity,
199
      '#context' => $context,
200
    );
201
    $build['entity'] = $this->buildEntityEmbedDisplayPlugin(
202
      $entity,
203
      $context['data-entity-embed-display'],
204
      $context['data-entity-embed-settings'],
205
      $context
206
    );
207
208
    // Maintain data-align if it is there.
209
    if (isset($context['data-align'])) {
210
      $build['#attributes']['data-align'] = $context['data-align'];
211
    }
212
    elseif ((isset($context['class']))) {
213
      $build['#attributes']['class'][] = $context['class'];
214
    }
215
216
    // Maintain data-caption if it is there.
217
    if (isset($context['data-caption'])) {
218
      $build['#attributes']['data-caption'] = $context['data-caption'];
219
    }
220
221
    // Make sure that access to the entity is respected.
222
    $build['#access'] = $entity->access('view', NULL, TRUE);
223
224
    // @todo Should this hook get invoked if $build is an empty array?
225
    $this->moduleHandler()->alter(array("{$context['data-entity-type']}_embed", 'entity_embed'), $build, $entity, $context);
226
    return $build;
227
  }
228
229
  /**
230
   * Builds the render array for an entity using an Entity Embed Display plugin.
231
   *
232
   * @param \Drupal\Core\Entity\EntityInterface $entity
233
   *   The entity to be rendered.
234
   * @param string $plugin_id
235
   *   The Entity Embed Display plugin ID.
236
   * @param array $plugin_configuration
237
   *   (optional) Array of plugin configuration values.
238
   * @param array $context
239
   *   (optional) Array of additional context values, usually the embed HTML
240
   *   tag's attributes.
241
   *
242
   * @return array
243
   *   A render array for the Entity Embed Display plugin.
244
   */
245
  protected function buildEntityEmbedDisplayPlugin(EntityInterface $entity, $plugin_id, array $plugin_configuration = array(), array $context = array()) {
246
    // Build the Entity Embed Display plugin.
247
    /** @var \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayBase $display */
248
    $display = $this->displayPluginManager()->createInstance($plugin_id, $plugin_configuration);
249
    $display->setContextValue('entity', $entity);
250
    $display->setAttributes($context);
251
252
    // Check if the Entity Embed Display plugin is accessible. This also checks
253
    // entity access, which is why we never call $entity->access() here.
254
    if (!$display->access()) {
255
      return array();
256
    }
257
258
    return $display->build();
259
  }
260
261
  /**
262
   * Returns the entity manager.
263
   *
264
   * @return \Drupal\Core\Entity\EntityManagerInterface
265
   *   The entity manager.
266
   */
267
  protected function entityManager() {
268
    if (!isset($this->entityManager)) {
269
      $this->entityManager = \Drupal::entityManager();
270
    }
271
    return $this->entityManager;
272
  }
273
274
  /**
275
   * Sets the entity manager service.
276
   *
277
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
278
   *   The entity manager service.
279
   *
280
   * @return self
281
   */
282
  public function setEntityManager(EntityManagerInterface $entity_manager) {
283
    $this->entityManager = $entity_manager;
284
    return $this;
285
  }
286
287
  /**
288
   * Returns the module handler.
289
   *
290
   * @return \Drupal\Core\Extension\ModuleHandlerInterface
291
   *   The module handler.
292
   */
293
  protected function moduleHandler() {
294
    if (!isset($this->moduleHandler)) {
295
      $this->moduleHandler = \Drupal::moduleHandler();
296
    }
297
    return $this->moduleHandler;
298
  }
299
300
  /**
301
   * Sets the module handler service.
302
   *
303
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
304
   *   The module handler service.
305
   *
306
   * @return self
307
   */
308
  public function setModuleHandler(ModuleHandlerInterface $module_handler) {
309
    $this->moduleHandler = $module_handler;
0 ignored issues
show
Documentation Bug introduced by
It seems like $module_handler of type object<Drupal\Core\Exten...ModuleHandlerInterface> is incompatible with the declared type object<Drupal\Core\Exten...oduleHandlerInterface.> of property $moduleHandler.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
310
    return $this;
311
  }
312
313
  /**
314
   * Returns the Entity Embed Display plugin manager.
315
   *
316
   * @return \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager
317
   *   The Entity Embed Display plugin manager.
318
   */
319
  protected function displayPluginManager() {
320
    if (!isset($this->displayPluginManager)) {
321
      $this->displayPluginManager = \Drupal::service('plugin.manager.entity_embed.display');
322
    }
323
    return $this->displayPluginManager;
324
  }
325
326
  /**
327
   * Sets the Entity Embed Display plugin manager service.
328
   *
329
   * @param \Drupal\entity_embed\EntityEmbedDisplay\EntityEmbedDisplayManager $display_plugin_manager
330
   *   The Entity Embed Display plugin manager service.
331
   *
332
   * @return self
333
   */
334
  public function setDisplayPluginManager(EntityEmbedDisplayManager $display_plugin_manager) {
335
    $this->displayPluginManager = $display_plugin_manager;
336
    return $this;
337
  }
338
339
}
340