From fa1b5ba5984ac3afa10d9db56c56491232199d44 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Mon, 3 Nov 2014 08:39:33 +0000
Subject: [PATCH] Issue #2346763 by yched, msonnabaum: Improve views field
 rendering performance by caching entity display objects.

---
 .../Drupal/Core/Entity/EntityViewBuilder.php  | 82 +++++++++++++------
 .../Entity/EntityViewBuilderInterface.php     |  4 +-
 .../field/src/Plugin/views/field/Field.php    |  4 -
 3 files changed, 60 insertions(+), 30 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 8330590f2494..a6688f42794d 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -9,13 +9,13 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\Core\Render\Element;
-use Drupal\Core\Entity\Entity\EntityViewDisplay;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -60,6 +60,15 @@ class EntityViewBuilder extends EntityHandlerBase implements EntityHandlerInterf
    */
   protected $languageManager;
 
+  /**
+   * The EntityViewDisplay objects created for individual field rendering.
+   *
+   * @see \Drupal\Core\Entity\EntityViewBuilder::getSingleFieldDisplay()
+   *
+   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface[]
+   */
+  protected $singleFieldDisplays;
+
   /**
    * Constructs a new EntityViewBuilder.
    *
@@ -393,32 +402,11 @@ protected function isViewModeCacheable($view_mode) {
    * {@inheritdoc}
    */
   public function viewField(FieldItemListInterface $items, $display_options = array()) {
-    $output = array();
     $entity = $items->getEntity();
     $field_name = $items->getFieldDefinition()->getName();
+    $display = $this->getSingleFieldDisplay($entity, $field_name, $display_options);
 
-    // Get the display object.
-    if (is_string($display_options)) {
-      $view_mode = $display_options;
-      $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
-      // Hide all fields except the current one.
-      foreach (array_keys($entity->getFieldDefinitions()) as $name) {
-        if ($name != $field_name) {
-          $display->removeComponent($name);
-        }
-      }
-    }
-    else {
-      $view_mode = '_custom';
-      $display = entity_create('entity_view_display', array(
-        'targetEntityType' => $entity->getEntityTypeId(),
-        'bundle' => $entity->bundle(),
-        'mode' => $view_mode,
-        'status' => TRUE,
-      ));
-      $display->setComponent($field_name, $display_options);
-    }
-
+    $output = array();
     $build = $display->build($entity);
     if (isset($build[$field_name])) {
       $output = $build[$field_name];
@@ -451,4 +439,50 @@ public function viewFieldItem(FieldItemInterface $item, $display = array()) {
     return $output;
   }
 
+  /**
+   * Returns an EntityViewDisplay for rendering an individual field.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   * @param string $field_name
+   *   The field name.
+   * @param string|array $display_options
+   *   The display options passed to the viewField() method.
+   *
+   * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+   */
+  protected function getSingleFieldDisplay($entity, $field_name, $display_options) {
+    if (is_string($display_options)) {
+      // View mode: use the Display configured for the view mode.
+      $view_mode = $display_options;
+      $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
+      // Hide all fields except the current one.
+      foreach (array_keys($entity->getFieldDefinitions()) as $name) {
+        if ($name != $field_name) {
+          $display->removeComponent($name);
+        }
+      }
+    }
+    else {
+      // Array of custom display options: use a runtime Display for the
+      // '_custom' view mode. Persist the displays created, to reduce the number
+      // of objects (displays and formatter plugins) created when rendering a
+      // series of fields individually for cases such as views tables.
+      $entity_type_id = $entity->getEntityTypeId();
+      $bundle = $entity->bundle();
+      $key = $entity_type_id . ':' . $bundle . ':' . $field_name . ':' . crc32(serialize($display_options));
+      if (!isset($this->singleFieldDisplays[$key])) {
+        $this->singleFieldDisplays[$key] = EntityViewDisplay::create(array(
+          'targetEntityType' => $entity_type_id,
+          'bundle' => $bundle,
+          'mode' => '_custom',
+          'status' => TRUE,
+        ))->setComponent($field_name, $display_options);
+      }
+      $display = $this->singleFieldDisplays[$key];
+    }
+
+    return $display;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index 27a1c8793c8e..c3e167a01351 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -106,7 +106,7 @@ public function resetCache(array $entities = NULL);
    *
    * @param \Drupal\Core\Field\FieldItemListInterface $items
    *   FieldItemList containing the values to be displayed.
-   * @param array $display_options
+   * @param string|array $display_options
    *  Can be either:
    *   - The name of a view mode. The field will be displayed according to the
    *     display settings specified for this view mode in the $field
@@ -137,7 +137,7 @@ public function viewField(FieldItemListInterface $items, $display_options = arra
    *
    * @param \Drupal\Core\Field\FieldItemInterface $item
    *   FieldItem to be displayed.
-   * @param array $display_options
+   * @param string|array $display_options
    *   Can be either the name of a view mode, or an array of display settings.
    *   See EntityViewBuilderInterface::viewField() for more information.
    *
diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php
index c05f6edf5bb9..6cb97ba45d87 100644
--- a/core/modules/field/src/Plugin/views/field/Field.php
+++ b/core/modules/field/src/Plugin/views/field/Field.php
@@ -740,10 +740,6 @@ public function getItems(ResultRow $values) {
       'type' => $this->options['type'],
       'settings' => $this->options['settings'],
       'label' => 'hidden',
-      // Pass the View object in the display so that fields can act on it.
-      'views_view' => $this->view,
-      'views_field' => $this,
-      'views_row_id' => $values->index,
     );
     $render_array = $entity->get($this->definition['field_name'])->view($display);
 
-- 
GitLab