EntityViewBuilder.php 8.04 KB
Newer Older
1
2
3
4
<?php

/**
 * @file
5
 * Contains \Drupal\Core\Entity\EntityViewBuilder.
6
7
8
9
 */

namespace Drupal\Core\Entity;

10
use Drupal\entity\Entity\EntityDisplay;
11
use Drupal\Core\Language\Language;
12

13
14
15
/**
 * Base class for entity view controllers.
 */
16
class EntityViewBuilder implements EntityViewBuilderInterface {
17
18
19
20
21
22
23
24

  /**
   * The type of entities for which this controller is instantiated.
   *
   * @var string
   */
  protected $entityType;

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  /**
   * The entity info array.
   *
   * @var array
   *
   * @see entity_get_info()
   */
  protected $entityInfo;

  /**
   * An array of view mode info for the type of entities for which this
   * controller is instantiated.
   *
   * @var array
   */
  protected $viewModesInfo;

  /**
   * The cache bin used to store the render cache.
   *
   * @todo Defaults to 'cache' for now, until http://drupal.org/node/1194136 is
   * fixed.
   *
   * @var string
   */
  protected $cacheBin = 'cache';

52
  public function __construct($entity_type) {
53
    $this->entityType = $entity_type;
54
    $this->entityInfo = entity_get_info($entity_type);
55
    $this->viewModesInfo = entity_get_view_modes($entity_type);
56
57
58
  }

  /**
59
   * {@inheritdoc}
60
   */
61
62
  public function buildContent(array $entities, array $displays, $view_mode, $langcode = NULL) {
    field_attach_prepare_view($this->entityType, $entities, $displays, $langcode);
63
64
65
66
67
68
69
70
71
72

    // Initialize the field item attributes for the fields set to be displayed.
    foreach ($entities as $entity) {
      // The entity can include fields that aren't displayed, and the display
      // can include components that aren't fields, so we want to iterate the
      // intersection of $entity->getProperties() and $display->getComponents().
      // However, the entity can have many more fields than are displayed, so we
      // avoid the cost of calling $entity->getProperties() by iterating the
      // intersection as follows.
      foreach ($displays[$entity->bundle()]->getComponents() as $name => $options) {
73
        if ($entity->hasField($name)) {
74
75
76
77
78
79
80
          foreach ($entity->get($name) as $item) {
            $item->_attributes = array();
          }
        }
      }
    }

81
    module_invoke_all('entity_prepare_view', $this->entityType, $entities, $displays, $view_mode);
82
83

    foreach ($entities as $entity) {
84
      // Remove previously built content, if exists.
85
86
87
88
      $entity->content = array(
        '#view_mode' => $view_mode,
      );
      $entity->content += field_attach_view($entity, $displays[$entity->bundle()], $langcode);
89
90
91
92
93
94
    }
  }

  /**
   * Provides entity-specific defaults to the build process.
   *
95
   * @param \Drupal\Core\Entity\EntityInterface $entity
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
   *   The entity for which the defaults should be provided.
   * @param string $view_mode
   *   The view mode that should be used.
   * @param string $langcode
   *   (optional) For which language the entity should be prepared, defaults to
   *   the current content language.
   *
   * @return array
   */
  protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) {
    $return = array(
      '#theme' => $this->entityType,
      "#{$this->entityType}" => $entity,
      '#view_mode' => $view_mode,
      '#langcode' => $langcode,
    );
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

    // Cache the rendered output if permitted by the view mode and global entity
    // type configuration. The isset() checks below are necessary because
    // 'default' is not an actual view mode.
    $view_mode_is_cacheable = !isset($this->viewModesInfo[$view_mode]) || (isset($this->viewModesInfo[$view_mode]) && $this->viewModesInfo[$view_mode]['cache']);
    if ($view_mode_is_cacheable && !$entity->isNew() && !isset($entity->in_preview) && $this->entityInfo['render_cache']) {
      $return['#cache'] = array(
        'keys' => array('entity_view', $this->entityType, $entity->id(), $view_mode),
        'granularity' => DRUPAL_CACHE_PER_ROLE,
        'bin' => $this->cacheBin,
        'tags' => array(
          $this->entityType . '_view' => TRUE,
          $this->entityType => array($entity->id()),
        ),
      );
    }

129
130
131
132
133
134
135
136
    return $return;
  }

  /**
   * Specific per-entity building.
   *
   * @param array $build
   *   The render array that is being created.
137
   * @param \Drupal\Core\Entity\EntityInterface $entity
138
   *   The entity to be prepared.
139
   * @param \Drupal\entity\Entity\EntityDisplay $display
140
141
   *   The entity_display object holding the display options configured for
   *   the entity components.
142
143
144
145
146
147
   * @param string $view_mode
   *   The view mode that should be used to prepare the entity.
   * @param string $langcode
   *   (optional) For which language the entity should be prepared, defaults to
   *   the current content language.
   */
148
  protected function alterBuild(array &$build, EntityInterface $entity, EntityDisplay $display, $view_mode, $langcode = NULL) { }
149
150

  /**
151
   * {@inheritdoc}
152
153
154
155
156
157
158
   */
  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
    $buildList = $this->viewMultiple(array($entity), $view_mode, $langcode);
    return $buildList[0];
  }

  /**
159
   * {@inheritdoc}
160
161
162
   */
  public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
    if (!isset($langcode)) {
163
      $langcode = language(Language::TYPE_CONTENT)->id;
164
    }
165
166
167
168
169

    // Build the view modes and display objects.
    $view_modes = array();
    $displays = array();
    $context = array('langcode' => $langcode);
170
    foreach ($entities as $entity) {
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
      $bundle = $entity->bundle();

      // Allow modules to change the view mode.
      $entity_view_mode = $view_mode;
      drupal_alter('entity_view_mode', $entity_view_mode, $entity, $context);
      // Store entities for rendering by view_mode.
      $view_modes[$entity_view_mode][$entity->id()] = $entity;

      // Load the corresponding display settings if not stored yet.
      if (!isset($displays[$entity_view_mode][$bundle])) {
        // Get the display object for this bundle and view mode.
        $display = entity_get_render_display($entity, $entity_view_mode);

        // Let modules alter the display.
        $display_context = array(
          'entity_type' => $this->entityType,
          'bundle' => $bundle,
          'view_mode' => $entity_view_mode,
        );
        drupal_alter('entity_display', $display, $display_context);

        $displays[$entity_view_mode][$bundle] = $display;
      }
    }

    foreach ($view_modes as $mode => $view_mode_entities) {
      $this->buildContent($view_mode_entities, $displays[$mode], $mode, $langcode);
    }
199
200
201
202
203
204

    $view_hook = "{$this->entityType}_view";
    $build = array('#sorted' => TRUE);
    $weight = 0;
    foreach ($entities as $key => $entity) {
      $entity_view_mode = isset($entity->content['#view_mode']) ? $entity->content['#view_mode'] : $view_mode;
205
206
207
      $display = $displays[$entity_view_mode][$entity->bundle()];
      module_invoke_all($view_hook, $entity, $display, $entity_view_mode, $langcode);
      module_invoke_all('entity_view', $entity, $display, $entity_view_mode, $langcode);
208
209
210
211
212
213

      $build[$key] = $entity->content;
      // We don't need duplicate rendering info in $entity->content.
      unset($entity->content);

      $build[$key] += $this->getBuildDefaults($entity, $entity_view_mode, $langcode);
214
215
216
217
218
219
220
221
222
      $this->alterBuild($build[$key], $entity, $display, $entity_view_mode, $langcode);

      // Assign the weights configured in the display.
      foreach ($display->getComponents() as $name => $options) {
        if (isset($build[$key][$name])) {
          $build[$key][$name]['#weight'] = $options['weight'];
        }
      }

223
224
      $build[$key]['#weight'] = $weight++;

225
226
      // Allow modules to modify the render array.
      drupal_alter(array($view_hook, 'entity_view'), $build[$key], $entity, $display);
227
228
229
230
    }

    return $build;
  }
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

  /**
   * {@inheritdoc}
   */
  public function resetCache(array $ids = NULL) {
    if (isset($ids)) {
      $tags = array();
      foreach ($ids as $entity_id) {
        $tags[$this->entityType][$entity_id] = $entity_id;
      }
      \Drupal::cache($this->cacheBin)->deleteTags($tags);
    }
    else {
      \Drupal::cache($this->cacheBin)->deleteTags(array($this->entityType . '_view' => TRUE));
    }
  }
247
}