Issue #2090509 by yched, swentel, Wim Leers: Optimize entity_get_render_display() for the case of 'multiple entity view'.

8.0.x
Nathaniel Catchpole 2014-02-10 14:50:13 +00:00
parent 51d9879c22
commit 9c88b1afce
8 changed files with 217 additions and 125 deletions

View File

@ -569,62 +569,6 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
return $display;
}
/**
* Returns the entity view display 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 entity view display 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_view_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_view_display', $display, $display_context);
return $display;
}
/**
* Returns the entity form display associated to a bundle and form mode.
*
@ -681,59 +625,6 @@ function entity_get_form_display($entity_type, $bundle, $form_mode) {
return $entity_form_display;
}
/**
* Returns the entity form display 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 entity 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.
*

View File

@ -10,6 +10,7 @@ namespace Drupal\Core\Entity;
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 @@ class EntityFormController extends FormBase implements EntityFormControllerInter
// 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.

View File

@ -9,9 +9,9 @@ namespace Drupal\Core\Entity;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\entity\Entity\EntityViewDisplay;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -193,7 +193,6 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
// 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 +207,10 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
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] = EntityViewDisplay::collectRenderDisplays($view_mode_entities, $mode);
$this->buildContent($view_mode_entities, $displays[$mode], $mode, $langcode);
}

View File

@ -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\Extension\ModuleHandlerInterface;
use Drupal\user\TempStoreFactory;
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 Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Builds and process a form for editing a single entity field.
@ -128,7 +129,7 @@ class EditFieldForm extends FormBase {
// @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');
}
/**

View File

@ -11,7 +11,7 @@ use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\edit\Access\EditEntityFieldAccessCheckInterface;
use Drupal\field\FieldInstanceInterface;
use Drupal\entity\Entity\EntityViewDisplay;
/**
* Generates in-place editing metadata for an entity field.
@ -78,7 +78,7 @@ class MetadataGenerator implements MetadataGeneratorInterface {
}
// Early-return if no editor is available.
$formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($field_name)->getPluginId();
$formatter_id = EntityViewDisplay::collectRenderDisplay($entity, $view_mode)->getRenderer($field_name)->getPluginId();
$editor_id = $this->editorSelector->getEditor($formatter_id, $items);
if (!isset($editor_id)) {
return array('access' => FALSE);

View File

@ -35,6 +35,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}
*/

View File

@ -7,6 +7,7 @@
namespace Drupal\entity\Entity;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\entity\EntityDisplayBase;
@ -35,6 +36,131 @@ class EntityViewDisplay extends EntityDisplayBase implements EntityViewDisplayIn
*/
protected $displayContext = 'view';
/**
* Returns the 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_view_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_view_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_view_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_view_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_view_display', $display, $display_context);
$displays_by_bundle[$bundle] = $display;
}
return $displays_by_bundle;
}
/**
* Returns the 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}
*/

View File

@ -7,6 +7,7 @@
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Template\Attribute;
use Drupal\entity\Entity\EntityViewDisplay;
/*
* 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 = EntityViewDisplay::collectRenderDisplay($entity, $view_mode)->getRenderer($field_name);
}
else {
$view_mode = '_custom';