diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index f4433c0..d73a0a6 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -89,41 +89,6 @@ public static function createInstance(ContainerInterface $container, EntityTypeI } /** - * {@inheritdoc} - */ - public function buildContent(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { - $entities_by_bundle = array(); - foreach ($entities as $id => $entity) { - // Initialize the field item attributes for the fields being displayed. - // The entity can include fields that are not displayed, and the display - // can include components that are not fields, so we want to act on the - // intersection. 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) { - if ($entity->hasField($name)) { - foreach ($entity->get($name) as $item) { - $item->_attributes = array(); - } - } - } - // Group the entities by bundle. - $entities_by_bundle[$entity->bundle()][$id] = $entity; - } - - // Invoke hook_entity_prepare_view(). - \Drupal::moduleHandler()->invokeAll('entity_prepare_view', array($this->entityTypeId, $entities, $displays, $view_mode)); - - // Let the displays build their render arrays. - foreach ($entities_by_bundle as $bundle => $bundle_entities) { - $display_build = $displays[$bundle]->buildMultiple($bundle_entities); - foreach ($bundle_entities as $id => $entity) { - $build[$id] = array_merge($build[$id], $display_build[$id]); - } - } - } - - /** * Provides entity-specific defaults to the build process. * * @param \Drupal\Core\Entity\EntityInterface $entity @@ -131,52 +96,24 @@ public function buildContent(array &$build, array $entities, array $displays, $v * @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 + * For which language the entity should be prepared, defaults to * the current content language. * * @return array */ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { - // Allow modules to change the view mode. - $context = array('langcode' => $langcode); - $this->moduleHandler->alter('entity_view_mode', $view_mode, $entity, $context); - $build = array( '#theme' => $this->entityTypeId, - '#pre_render' => array(array($this, 'entityViewBuilderBuildSingle')), + '#pre_render' => array(array($this, 'buildEntity')), "#{$this->entityTypeId}" => $entity, '#view_mode' => $view_mode, '#langcode' => $langcode, - ); - - // Collect cache defaults for this entity. - $build = $this->getBuildCacheDefaults($build, $entity, $view_mode, $langcode); - - return $build; - } - - /** - * Provides entity-specific cache defaults to the build process. - * - * @param array $build - * A renderable array containing build information and context for an - * entity view. - * @param \Drupal\Core\Entity\EntityInterface $entity - * 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 getBuildCacheDefaults(array $build, EntityInterface $entity, $view_mode, $langcode) { - $build += array( + // Collect cache defaults for this entity. '#cache' => array( 'tags' => NestedArray::mergeDeep($this->getCacheTag(), $entity->getCacheTag()), ), ); + // Cache the rendered output if permitted by the view mode and global entity // type configuration. if ($this->isViewModeCacheable($view_mode) && !$entity->isNew() && $entity->isDefaultRevision() && $this->entityType->isRenderCacheable()) { @@ -196,6 +133,67 @@ protected function getBuildCacheDefaults(array $build, EntityInterface $entity, $build['#cache']['keys'][] = $langcode; } } + + return $build; + } + + /** + * Specific per-entity building. + * + * @param array $build + * The render array that is being created. + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity to be prepared. + * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display + * The entity view display holding the display options configured for the + * entity components. + * @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. + */ + protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) { } + + /** + * {@inheritdoc} + */ + public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + $buildList = $this->viewMultiple(array($entity), $view_mode, $langcode); + return $buildList[0]; + } + + /** + * {@inheritdoc} + */ + public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { + if (!isset($langcode)) { + $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id; + } + + // Allow modules to change the view mode. + $context = array('langcode' => $langcode); + $this->moduleHandler->alter('entity_view_mode', $view_mode, $entity, $context); + + $build = array( + '#sorted' => TRUE, + '#pre_render' => array(array($this, 'buildEntityMultiple')), + '#langcode' => $langcode, + ); + $weight = 0; + foreach ($entities as $key => $entity) { + // Ensure that from now on we are dealing with the proper translation + // object. + $entity = $this->entityManager->getTranslationFromContext($entity, $langcode); + + // Set render defaults. + $build[$key] = $this->getBuildDefaults($entity, $view_mode, $langcode); + $entityType = $this->entityTypeId; + \Drupal::moduleHandler()->alter(array($entityType . '_defaults', 'entity_defaults'), $build[$key], $entity, $view_mode, $langcode); + + $build[$key]['#weight'] = $weight++; + } + return $build; } @@ -218,27 +216,13 @@ protected function getBuildCacheDefaults(array $build, EntityInterface $entity, * * @see drupal_render() */ - public function entityViewBuilderBuildSingle(array $build) { - $entity = $build["#{$this->entityTypeId}"]; - $bundle = $entity->bundle(); - $id = $entity->id(); - $view_hook = "{$this->entityTypeId}_view"; - $view_mode = $build['#view_mode']; - $langcode = $build['#langcode']; - - // Build all visible components. - $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); - // EntityViewBuilderInterface::buildContent() works on multiple entities - // simultaneously, yet $build is only for a single entity. Therefore, - // perform the necessary transformations before and after calling it. - $build_per_entity = array($id => $build); - $this->buildContent($build_per_entity, array($id => $entity), array($bundle => $display), $view_mode, $langcode); - $build = $build_per_entity[$id]; - - // Allow for alterations while building, before rendering. - $this->callViewHooks($build, $entity, $display, $view_mode, $langcode); - - return $build; + public function buildEntity(array $build) { + $buildlist = array( + '#langcode' => $build['#langcode'], + ); + $buildlist[] = $build; + $buildlist = $this->buildEntityMultiple($buildlist); + return $buildlist[0]; } /** @@ -263,7 +247,7 @@ public function entityViewBuilderBuildSingle(array $build) { * * @see drupal_render() */ - public function entityViewBuilderBuildMultiple(array $build) { + public function buildEntityMultiple(array $build) { // Build the view modes and display objects. $view_modes = array(); $langcode = $build['#langcode']; @@ -272,7 +256,7 @@ public function entityViewBuilderBuildMultiple(array $build) { // Find the keys for the ContentEntities in the build; Store entities for // rendering by view_mode. $children = Element::children($build); - foreach($children as $key) { + foreach ($children as $key) { if (isset($build[$key][$entity_type_key])) { $entity = $build[$key][$entity_type_key]; if ($entity instanceof ContentEntityInterface) { @@ -286,118 +270,78 @@ public function entityViewBuilderBuildMultiple(array $build) { $displays = EntityViewDisplay::collectRenderDisplays($view_mode_entities, $view_mode); $this->buildContent($build, $view_mode_entities, $displays, $view_mode, $langcode); foreach (array_keys($view_mode_entities) as $key) { - // Remove the single item pre_render function from the multiple build items. + // Remove the single item pre_render function. The renderable arrays + // were just built in the call to $this->buildContent above. Removing + // the pre_render functions prevents the renderable arrays from being + // built again when drupal_render() recurses into the child elements + // of this list of renderables. $pre_render = $build[$key]['#pre_render'] ?: array(); foreach ($pre_render as $index => $callable) { - if (is_array($callable) && $callable[1] === 'entityViewBuilderBuildSingle') { + if (is_array($callable) && $callable[1] === 'buildEntity') { unset($build[$key]['#pre_render'][$index]); } } // Allow for alterations while building, before rendering. $entity = $build[$key][$entity_type_key]; $display = $displays[$entity->bundle()]; - $this->callViewHooks($build[$key], $entity, $display, $view_mode, $langcode); - } - } - - return $build; - } + $view_hook = "{$this->entityTypeId}_view"; - /** - * Wraps calls to the various hook invocations for entity view building. - * - * The hook invocations are shared between entityViewBuilderBuildSingle() and - * entityViewBuilderBuildMultiple(). - * - * @param array &$build - * The render array that is being created. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to be prepared. - * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display - * The entity view display holding the display options configured for the - * entity components. - * @param string $view_mode - * The view mode that should be used to prepare the entity. - * @param string $langcode - * For which language the entity should be prepared, defaults to - * the current content language. - */ - private function callViewHooks(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) { - $view_hook = "{$this->entityTypeId}_view"; + $this->moduleHandler()->invokeAll($view_hook, array(&$build, $entity, $display, $view_mode, $langcode)); + $this->moduleHandler()->invokeAll('entity_view', array(&$build, $entity, $display, $view_mode, $langcode)); - $this->moduleHandler()->invokeAll($view_hook, array(&$build, $entity, $display, $view_mode, $langcode)); - $this->moduleHandler()->invokeAll('entity_view', array(&$build, $entity, $display, $view_mode, $langcode)); + $this->alterBuild($build, $entity, $display, $view_mode, $langcode); - $this->alterBuild($build, $entity, $display, $view_mode, $langcode); + // Assign the weights configured in the display. + // @todo: Once https://drupal.org/node/1875974 provides the missing API, + // only do it for 'extra fields', since other components have been + // taken care of in EntityViewDisplay::buildMultiple(). + foreach ($display->getComponents() as $name => $options) { + if (isset($build[$name])) { + $build['#weight'] = $options['weight']; + } + } - // Assign the weights configured in the display. - // @todo: Once https://drupal.org/node/1875974 provides the missing API, - // only do it for 'extra fields', since other components have been taken - // care of in EntityViewDisplay::buildMultiple(). - foreach ($display->getComponents() as $name => $options) { - if (isset($build[$name])) { - $build['#weight'] = $options['weight']; + // Allow modules to modify the render array. + \Drupal::moduleHandler()->alter(array($view_hook, 'entity_view'), $build, $entity, $display); } } - // Allow modules to modify the render array. - \Drupal::moduleHandler()->alter(array($view_hook, 'entity_view'), $build, $entity, $display); - } - - /** - * Specific per-entity building. - * - * @param array $build - * The render array that is being created. - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to be prepared. - * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display - * The entity view display holding the display options configured for the - * entity components. - * @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. - */ - protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode = NULL) { } - - /** - * {@inheritdoc} - */ - public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { - $buildList = $this->viewMultiple(array($entity), $view_mode, $langcode); - return $buildList[0]; + return $build; } /** * {@inheritdoc} */ - public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) { - if (!isset($langcode)) { - $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id; + public function buildContent(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL) { + $entities_by_bundle = array(); + foreach ($entities as $id => $entity) { + // Initialize the field item attributes for the fields being displayed. + // The entity can include fields that are not displayed, and the display + // can include components that are not fields, so we want to act on the + // intersection. 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) { + if ($entity->hasField($name)) { + foreach ($entity->get($name) as $item) { + $item->_attributes = array(); + } + } + } + // Group the entities by bundle. + $entities_by_bundle[$entity->bundle()][$id] = $entity; } - $build = array( - '#sorted' => TRUE, - '#pre_render' => array(array($this, 'entityViewBuilderBuildMultiple')), - '#langcode' => $langcode, - ); - $weight = 0; - foreach ($entities as $key => $entity) { - // Ensure that from now on we are dealing with the proper translation - // object. - $entity = $this->entityManager->getTranslationFromContext($entity, $langcode); - - // Set render defaults. - $build[$key] = $this->getBuildDefaults($entity, $view_mode, $langcode); - $entityType = $this->entityTypeId; - \Drupal::moduleHandler()->alter(array($entityType . '_defaults', 'entity_defaults'), $build[$key], $entity, $view_mode, $langcode); + // Invoke hook_entity_prepare_view(). + \Drupal::moduleHandler()->invokeAll('entity_prepare_view', array($this->entityTypeId, $entities, $displays, $view_mode)); - $build[$key]['#weight'] = $weight++; + // Let the displays build their render arrays. + foreach ($entities_by_bundle as $bundle => $bundle_entities) { + $display_build = $displays[$bundle]->buildMultiple($bundle_entities); + foreach ($bundle_entities as $id => $entity) { + $build[$id] = array_merge($build[$id], $display_build[$id]); + } } - - return $build; } /** diff --git a/core/lib/Drupal/Core/Field/FormatterBase.php b/core/lib/Drupal/Core/Field/FormatterBase.php index 3b8b19b..eeb94a3 100644 --- a/core/lib/Drupal/Core/Field/FormatterBase.php +++ b/core/lib/Drupal/Core/Field/FormatterBase.php @@ -92,7 +92,7 @@ public function view(FieldItemListInterface $items) { '#formatter' => $this->getPluginId(), ); - $addition[$field_name] = array_merge($info, $elements); + $addition = array_merge($info, $elements); } return $addition; diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedViewBuilder.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedViewBuilder.php index 5066eaa..853122f 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/FeedViewBuilder.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedViewBuilder.php @@ -18,7 +18,7 @@ class FeedViewBuilder extends EntityViewBuilder { /** * {@inheritdoc} */ - protected function getBuildDefaults(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode); $defaults['#theme'] = 'aggregator_feed_source'; return $defaults; diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemViewBuilder.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemViewBuilder.php index da69ecf..7d3f919 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/ItemViewBuilder.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemViewBuilder.php @@ -18,7 +18,7 @@ class ItemViewBuilder extends EntityViewBuilder { /** * {@inheritdoc} */ - protected function getBuildDefaults(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode); // Use a different template for the summary view mode. diff --git a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php index 4016be9..d919193 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityViewDisplay.php @@ -233,10 +233,7 @@ public function buildMultiple(array $entities) { if (!isset($build[$key][$field_name])) { $build[$key][$field_name] = array(); } - // @todo, maybe just return the contents of view without a wrapper and the subsequent need to reference - // a $field_name index? - $built_field = $formatter->view($items); - $build[$key][$field_name] += (!empty($built_field[$field_name])) ? $built_field[$field_name] : array(); + $build[$key][$field_name] += $formatter->view($items); $build[$key][$field_name]['#access'] = $items->access('view'); } } diff --git a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php index 0f6a800..53d909e 100644 --- a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php +++ b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php @@ -50,7 +50,7 @@ public function buildContent(array &$build, array $entities, array $displays, $v ), ), '#markup' => drupal_render_cache_generate_placeholder($callback, $context, $context['token']), - ); + ); // Add Language field text element to node render array. @@ -69,7 +69,7 @@ public function buildContent(array &$build, array $entities, array $displays, $v /** * {@inheritdoc} */ - protected function getBuildDefaults(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) { + protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langcode) { $defaults = parent::getBuildDefaults($entity, $view_mode, $langcode); // Don't cache nodes that are in 'preview' mode.