diff --git a/core/includes/entity.inc b/core/includes/entity.inc index 98acf56..eb108ff 100644 --- a/core/includes/entity.inc +++ b/core/includes/entity.inc @@ -595,62 +595,6 @@ function entity_get_display($entity_type, $bundle, $view_mode) { } /** - * Returns the entity_display object used to render an entity. - * - * Depending on the configuration of the view mode for the bundle, this can be - * either the display object associated to the view mode, or the 'default' - * display. - * - * This function should only be used internally when rendering an entity. When - * assigning suggested display options for a component in a given view mode, - * entity_get_display() should be used instead, in order to avoid inadvertently - * modifying the output of other view modes that might happen to use the - * 'default' display too. Those options will then be effectively applied only - * if the view mode is configured to use them. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity being rendered. - * @param string $view_mode - * The view mode being rendered. - * - * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface - * The display object that should be used to render the entity. - * - * @see entity_get_display(). - */ -function entity_get_render_display(EntityInterface $entity, $view_mode) { - $entity_type = $entity->getEntityTypeId(); - $bundle = $entity->bundle(); - $render_view_mode = 'default'; - - // Fall back to the default display if the display for the view mode does - // not exist or is disabled. - if ($view_mode != 'default') { - $ids = array( - 'default' => $entity_type . '.' . $bundle . '.default', - $view_mode => $entity_type . '.' . $bundle . '.' . $view_mode, - ); - $entity_displays = entity_load_multiple('entity_display', $ids); - if (isset($entity_displays[$ids[$view_mode]]) && $entity_displays[$ids[$view_mode]]->status()) { - $render_view_mode = $view_mode; - } - } - - $display = entity_get_display($entity_type, $bundle, $render_view_mode); - $display->originalMode = $view_mode; - - // Let modules alter the display. - $display_context = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'view_mode' => $view_mode, - ); - drupal_alter('entity_display', $display, $display_context); - - return $display; -} - -/** * Returns the entity_form_display object associated to a bundle and form mode. * * The function reads the entity_form_display object from the current @@ -707,58 +651,6 @@ function entity_get_form_display($entity_type, $bundle, $form_mode) { } /** - * Returns the entity_form_display object used to render an entity form. - * - * This function should only be used internally when rendering an entity form. - * When assigning suggested form display options for a component in a given form - * mode, entity_get_form_display() should be used instead, in order to avoid - * inadvertently modifying the output of other form modes that might happen to - * use the 'default' form display too. Those options will then be effectively - * applied only if the form mode is configured to use them. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity for which the form is being rendered. - * @param string $form_mode - * The form mode being rendered. - * - * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface - * The form display object that should be used to render the entity form. - * - * @see entity_get_form_display(). - */ -function entity_get_render_form_display(EntityInterface $entity, $form_mode) { - $entity_type = $entity->getEntityTypeId(); - $bundle = $entity->bundle(); - $render_form_mode = 'default'; - - // Look at the default form display and form display for the view mode, and - // fallback to the former if the latter does not exist is disabled. - if ($form_mode != 'default') { - $ids = array( - 'default' => $entity_type . '.' . $bundle . '.default', - $form_mode => $entity_type . '.' . $bundle . '.' . $form_mode, - ); - $entity_form_displays = entity_load_multiple('entity_form_display', $ids); - if (isset($entity_form_displays[$ids[$form_mode]]) && $entity_form_displays[$ids[$form_mode]]->status()) { - $render_form_mode = $form_mode; - } - } - - $form_display = entity_get_form_display($entity_type, $bundle, $render_form_mode); - $form_display->originalMode = $form_mode; - - // Let modules alter the form display. - $form_display_context = array( - 'entity_type' => $entity_type, - 'bundle' => $bundle, - 'form_mode' => $form_mode, - ); - drupal_alter('entity_form_display', $form_display, $form_display_context); - - return $form_display; -} - -/** * Generic access callback for entity pages. * * @param \Drupal\Core\Entity\EntityInterface $entity diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php index 342d395..440aa50 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormController.php +++ b/core/lib/Drupal/Core/Entity/EntityFormController.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\Display\EntityFormDisplayInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\entity\Entity\EntityFormDisplay; /** * Base class for entity form controllers. @@ -121,7 +122,7 @@ protected function init(array &$form_state) { // Prepare the entity to be presented in the entity form. $this->prepareEntity(); - $form_display = entity_get_render_form_display($this->entity, $this->getOperation()); + $form_display = EntityFormDisplay::collectRenderDisplay($this->entity, $this->getOperation()); $this->setFormDisplay($form_display, $form_state); // Invoke the prepare form hooks. diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 69bb693..55d60e3 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -12,6 +12,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\entity\Entity\EntityDisplay; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -193,7 +194,6 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la // Build the view modes and display objects. $view_modes = array(); - $displays = array(); $context = array('langcode' => $langcode); foreach ($entities as $key => $entity) { $bundle = $entity->bundle(); @@ -208,14 +208,10 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la 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; - - // Get the corresponding display settings. - if (!isset($displays[$entity_view_mode][$bundle])) { - $displays[$entity_view_mode][$bundle] = entity_get_render_display($entity, $entity_view_mode); - } } foreach ($view_modes as $mode => $view_mode_entities) { + $displays[$mode] = EntityDisplay::collectRenderDisplays($view_mode_entities, $mode); $this->buildContent($view_mode_entities, $displays[$mode], $mode, $langcode); } diff --git a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php index f6f5a94..e930206 100644 --- a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php +++ b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php @@ -7,13 +7,14 @@ namespace Drupal\edit\Form; -use Drupal\Core\Form\FormBase; -use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; +use Drupal\Core\Entity\EntityChangedInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Form\FormBase; +use Drupal\entity\Entity\EntityFormDisplay; use Drupal\user\TempStoreFactory; -use Drupal\Core\Entity\EntityChangedInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Builds and process a form for editing a single entity field. @@ -128,7 +129,7 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name // @todo Allow the usage of different form modes by exposing a hook and the // UI for them. - $form_state['form_display'] = entity_get_render_form_display($entity, 'default'); + $form_state['form_display'] = EntityFormDisplay::collectRenderDisplay($entity, 'default'); } /** diff --git a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php index e34e1dc..5096e28 100644 --- a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php +++ b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\edit\Access\EditEntityFieldAccessCheckInterface; +use Drupal\entity\Entity\EntityDisplay; use Drupal\field\FieldInstanceInterface; /** @@ -78,7 +79,7 @@ public function generateFieldMetadata(FieldItemListInterface $items, $view_mode) } // Early-return if no editor is available. - $formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($field_name)->getPluginId(); + $formatter_id = EntityDisplay::collectRenderDisplay($entity, $view_mode)->getRenderer($field_name)->getPluginId(); $editor_id = $this->editorSelector->getEditor($formatter_id, $items); if (!isset($editor_id)) { return array('access' => FALSE); diff --git a/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php b/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php index baf1cbd..f93f634 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityDisplay.php @@ -7,6 +7,7 @@ namespace Drupal\entity\Entity; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\entity\EntityDisplayBase; @@ -36,6 +37,131 @@ class EntityDisplay extends EntityDisplayBase implements EntityViewDisplayInterf protected $displayContext = 'view'; /** + * Returns the entity_display objects used to render a set of entities. + * + * Depending on the configuration of the view mode for each bundle, this can + * be either the display object associated to the view mode, or the 'default' + * display. + * + * This method should only be used internally when rendering an entity. When + * assigning suggested display options for a component in a given view mode, + * entity_get_display() should be used instead, in order to avoid + * inadvertently modifying the output of other view modes that might happen to + * use the 'default' display too. Those options will then be effectively + * applied only if the view mode is configured to use them. + * + * hook_entity_display_alter() is invoked on each display, allowing 3rd party + * code to alter the display options held in the display before they are used + * to generate render arrays. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The entities being rendered. They should all be of the same entity type. + * @param string $view_mode + * The view mode being rendered. + * + * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface[] + * The display objects to use to render the entities, keyed by entity + * bundle. + * + * @see entity_get_display() + * @see hook_entity_display_alter() + */ + public static function collectRenderDisplays($entities, $view_mode) { + if (empty($entities)) { + return array(); + } + + // Collect entity type and bundles. + $entity_type = current($entities)->getEntityTypeId(); + $bundles = array(); + foreach ($entities as $entity) { + $bundles[$entity->bundle()] = TRUE; + } + $bundles = array_keys($bundles); + + // For each bundle, check the existence and status of: + // - the display for the view mode, + // - the 'default' display. + $candidate_ids = array(); + foreach ($bundles as $bundle) { + if ($view_mode != 'default') { + $candidate_ids[$bundle][] = $entity_type . '.' . $bundle . '.' . $view_mode; + } + $candidate_ids[$bundle][] = $entity_type . '.' . $bundle . '.default'; + } + $results = \Drupal::entityQuery('entity_display') + ->condition('id', NestedArray::mergeDeepArray($candidate_ids)) + ->condition('status', TRUE) + ->execute(); + + // For each bundle, select the first valid candidate display, if any. + $load_ids = array(); + foreach ($bundles as $bundle) { + foreach ($candidate_ids[$bundle] as $candidate_id) { + if (isset($results[$candidate_id])) { + $load_ids[$bundle] = $candidate_id; + break; + } + } + } + + // Load the selected displays. + $storage = \Drupal::entityManager()->getStorageController('entity_display'); + $displays = $storage->loadMultiple($load_ids); + + $displays_by_bundle = array(); + foreach ($bundles as $bundle) { + // Use the selected display if any, or create a fresh runtime object. + if (isset($load_ids[$bundle])) { + $display = $displays[$load_ids[$bundle]]; + } + else { + $display = $storage->create(array( + 'targetEntityType' => $entity_type, + 'bundle' => $bundle, + 'mode' => $view_mode, + 'status' => TRUE, + )); + } + + // Let the display know which view mode was originally requested. + $display->originalMode = $view_mode; + + // Let modules alter the display. + $display_context = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'view_mode' => $view_mode, + ); + \Drupal::moduleHandler()->alter('entity_display', $display, $display_context); + + $displays_by_bundle[$bundle] = $display; + } + + return $displays_by_bundle; + } + + /** + * Returns the entity_display object used to render an entity. + * + * See the collectRenderDisplays() method for details. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity being rendered. + * @param string $view_mode + * The view mode. + * + * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface + * The display object that should be used to render the entity. + * + * @see \Drupal\entity\Entity\EntityDisplay::collectRenderDisplays() + */ + public static function collectRenderDisplay($entity, $view_mode) { + $displays = static::collectRenderDisplays(array($entity), $view_mode); + return $displays[$entity->bundle()]; + } + + /** * {@inheritdoc} */ public function __construct(array $values, $entity_type) { diff --git a/core/modules/entity/lib/Drupal/entity/Entity/EntityFormDisplay.php b/core/modules/entity/lib/Drupal/entity/Entity/EntityFormDisplay.php index ec22794..3222af7 100644 --- a/core/modules/entity/lib/Drupal/entity/Entity/EntityFormDisplay.php +++ b/core/modules/entity/lib/Drupal/entity/Entity/EntityFormDisplay.php @@ -36,6 +36,83 @@ class EntityFormDisplay extends EntityDisplayBase implements EntityFormDisplayIn protected $displayContext = 'form'; /** + * Returns the entity_form_display object used to build an entity form. + * + * Depending on the configuration of the form mode for the entity bundle, this + * can be either the display object associated to the form mode, or the + * 'default' display. + * + * This method should only be used internally when rendering an entity form. + * When assigning suggested display options for a component in a given form + * mode, entity_get_form_display() should be used instead, in order to avoid + * inadvertently modifying the output of other form modes that might happen to + * use the 'default' display too. Those options will then be effectively + * applied only if the form mode is configured to use them. + * + * hook_entity_form_display_alter() is invoked on each display, allowing 3rd + * party code to alter the display options held in the display before they are + * used to generate render arrays. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity for which the form is being built. + * @param string $form_mode + * The form mode. + * + * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface + * The display object that should be used to build the entity form. + * + * @see entity_get_form_display() + * @see hook_entity_form_display_alter() + */ + public static function collectRenderDisplay($entity, $form_mode) { + $entity_type = $entity->getEntityTypeId(); + $bundle = $entity->bundle(); + + // Check the existence and status of: + // - the display for the form mode, + // - the 'default' display. + if ($form_mode != 'default') { + $candidate_ids[] = $entity_type . '.' . $bundle . '.' . $form_mode; + } + $candidate_ids[] = $entity_type . '.' . $bundle . '.default'; + $results = \Drupal::entityQuery('entity_form_display') + ->condition('id', $candidate_ids) + ->condition('status', TRUE) + ->execute(); + + // Load the first valid candidate display, if any. + $storage = \Drupal::entityManager()->getStorageController('entity_form_display'); + foreach ($candidate_ids as $candidate_id) { + if (isset($results[$candidate_id])) { + $display = $storage->load($candidate_id); + break; + } + } + // Else create a fresh runtime object. + if (empty($display)) { + $display = $storage->create(array( + 'targetEntityType' => $entity_type, + 'bundle' => $bundle, + 'mode' => $form_mode, + 'status' => TRUE, + )); + } + + // Let the display know which form mode was originally requested. + $display->originalMode = $form_mode; + + // Let modules alter the display. + $display_context = array( + 'entity_type' => $entity_type, + 'bundle' => $bundle, + 'form_mode' => $form_mode, + ); + \Drupal::moduleHandler()->alter('entity_form_display', $display, $display_context); + + return $display; + } + + /** * {@inheritdoc} */ public function __construct(array $values, $entity_type) { diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 0aaffb5..eabd4c2 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -7,6 +7,7 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Template\Attribute; +use Drupal\entity\Entity\EntityDisplay; /* * Load all public Field API functions. Drupal currently has no @@ -428,7 +429,7 @@ function field_view_field(ContentEntityInterface $entity, $field_name, $display_ // Get the formatter object. if (is_string($display_options)) { $view_mode = $display_options; - $formatter = entity_get_render_display($entity, $view_mode)->getRenderer($field_name); + $formatter = EntityDisplay::collectRenderDisplay($entity, $view_mode)->getRenderer($field_name); } else { $view_mode = '_custom';