From bf42d739aaec307eb16c171be684cf1e1f0055e4 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Fri, 21 Oct 2016 13:57:58 +0100
Subject: [PATCH] Issue #2408667 by pguillard, mpdonadio, marvin_B8, Xano,
 geertvd, lomasr, dawehner, yched: Rename Field.php to EntityField.php

---
 .../comment/src/Plugin/views/field/Depth.php  |    4 +-
 .../src/Plugin/views/field/TermName.php       |    4 +-
 .../src/Entity/Render/EntityFieldRenderer.php |    8 +-
 .../src/Plugin/views/field/EntityField.php    | 1073 +++++++++++++++++
 .../views/src/Plugin/views/field/Field.php    | 1070 +---------------
 .../src/Tests/Handler/FieldGroupRowsTest.php  |    2 +-
 .../Update/EntityViewsDataUpdateTest.php      |    6 +-
 .../src/Tests/ViewResultAssertionTrait.php    |    4 +-
 .../src/Kernel/Handler/FieldFieldTest.php     |   40 +-
 .../tests/src/Unit/Plugin/field/FieldTest.php |   36 +-
 10 files changed, 1129 insertions(+), 1118 deletions(-)
 create mode 100644 core/modules/views/src/Plugin/views/field/EntityField.php

diff --git a/core/modules/comment/src/Plugin/views/field/Depth.php b/core/modules/comment/src/Plugin/views/field/Depth.php
index 8136e2ab4e28..04b43c766520 100644
--- a/core/modules/comment/src/Plugin/views/field/Depth.php
+++ b/core/modules/comment/src/Plugin/views/field/Depth.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\comment\Plugin\views\field;
 
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 use Drupal\views\ResultRow;
 
 /**
@@ -12,7 +12,7 @@
  *
  * @ViewsField("comment_depth")
  */
-class Depth extends Field {
+class Depth extends EntityField {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/taxonomy/src/Plugin/views/field/TermName.php b/core/modules/taxonomy/src/Plugin/views/field/TermName.php
index 20bc3c6f3ffc..2fcd5299a77c 100644
--- a/core/modules/taxonomy/src/Plugin/views/field/TermName.php
+++ b/core/modules/taxonomy/src/Plugin/views/field/TermName.php
@@ -3,7 +3,7 @@
 namespace Drupal\taxonomy\Plugin\views\field;
 
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 use Drupal\views\ResultRow;
 
 /**
@@ -13,7 +13,7 @@
  *
  * @ViewsField("term_name")
  */
-class TermName extends Field {
+class TermName extends EntityField {
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
index 1b05c1b2a360..3c38da176038 100644
--- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
+++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php
@@ -7,7 +7,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 use Drupal\views\Plugin\views\query\QueryPluginBase;
 use Drupal\views\ResultRow;
 use Drupal\views\ViewExecutable;
@@ -111,13 +111,13 @@ public function query(QueryPluginBase $query, $relationship = NULL) {
    *
    * @param \Drupal\views\ResultRow $row
    *   A single row of the query result.
-   * @param \Drupal\views\Plugin\views\field\Field $field
+   * @param \Drupal\views\Plugin\views\field\EntityField $field
    *   (optional) A field to be rendered.
    *
    * @return array
    *   A renderable array for the entity data contained in the result row.
    */
-  public function render(ResultRow $row, Field $field = NULL) {
+  public function render(ResultRow $row, EntityField $field = NULL) {
     // The method is called for each field in each result row. In order to
     // leverage multiple-entity building of formatter output, we build the
     // render arrays for all fields in all rows on the first call.
@@ -259,7 +259,7 @@ protected function buildFields(array $values) {
   protected function getRenderableFieldIds() {
     $field_ids = [];
     foreach ($this->view->field as $field_id => $field) {
-      if ($field instanceof Field && $field->relationship == $this->relationship) {
+      if ($field instanceof EntityField && $field->relationship == $this->relationship) {
         $field_ids[] = $field_id;
       }
     }
diff --git a/core/modules/views/src/Plugin/views/field/EntityField.php b/core/modules/views/src/Plugin/views/field/EntityField.php
new file mode 100644
index 000000000000..4c8032d3eb5f
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/field/EntityField.php
@@ -0,0 +1,1073 @@
+<?php
+
+namespace Drupal\views\Plugin\views\field;
+
+use Drupal\Component\Plugin\DependentPluginInterface;
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Form\FormHelper;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\PluginDependencyTrait;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\views\FieldAPIHandlerTrait;
+use Drupal\views\Entity\Render\EntityFieldRenderer;
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\ResultRow;
+use Drupal\views\ViewExecutable;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * A field that displays entity field data.
+ *
+ * @ingroup views_field_handlers
+ *
+ * @ViewsField("field")
+ */
+class EntityField extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface {
+
+  use FieldAPIHandlerTrait;
+  use PluginDependencyTrait;
+
+  /**
+   * An array to store field renderable arrays for use by renderItems().
+   *
+   * @var array
+   */
+  public $items = array();
+
+  /**
+   * Does the field supports multiple field values.
+   *
+   * @var bool
+   */
+  public $multiple;
+
+  /**
+   * Does the rendered fields get limited.
+   *
+   * @var bool
+   */
+  public $limit_values;
+
+  /**
+   * A shortcut for $view->base_table.
+   *
+   * @var string
+   */
+  public $base_table;
+
+  /**
+   * An array of formatter options.
+   *
+   * @var array
+   */
+  protected $formatterOptions;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The field formatter plugin manager.
+   *
+   * @var \Drupal\Core\Field\FormatterPluginManager
+   */
+  protected $formatterPluginManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * The field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypePluginManager;
+
+  /**
+   * Static cache for ::getEntityFieldRenderer().
+   *
+   * @var \Drupal\views\Entity\Render\EntityFieldRenderer
+   */
+  protected $entityFieldRenderer;
+
+  /**
+   * Constructs a \Drupal\field\Plugin\views\field\Field object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The field formatter plugin manager.
+   * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
+   *   The field formatter plugin manager.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_plugin_manager
+   *   The field plugin type manager.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->entityManager = $entity_manager;
+    $this->formatterPluginManager = $formatter_plugin_manager;
+    $this->fieldTypePluginManager = $field_type_plugin_manager;
+    $this->languageManager = $language_manager;
+    $this->renderer = $renderer;
+
+    // @todo Unify 'entity field'/'field_name' instead of converting back and
+    //   forth. https://www.drupal.org/node/2410779
+    if (isset($this->definition['entity field'])) {
+      $this->definition['field_name'] = $this->definition['entity field'];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity.manager'),
+      $container->get('plugin.manager.field.formatter'),
+      $container->get('plugin.manager.field.field_type'),
+      $container->get('language_manager'),
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
+    parent::init($view, $display, $options);
+
+    $this->multiple = FALSE;
+    $this->limit_values = FALSE;
+
+    $field_definition = $this->getFieldDefinition();
+    $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality();
+    if ($field_definition->getFieldStorageDefinition()->isMultiple()) {
+      $this->multiple = TRUE;
+
+      // If "Display all values in the same row" is FALSE, then we always limit
+      // in order to show a single unique value per row.
+      if (!$this->options['group_rows']) {
+        $this->limit_values = TRUE;
+      }
+
+      // If "First and last only" is chosen, limit the values
+      if (!empty($this->options['delta_first_last'])) {
+        $this->limit_values = TRUE;
+      }
+
+      // Otherwise, we only limit values if the user hasn't selected "all", 0, or
+      // the value matching field cardinality.
+      if ((($this->options['delta_limit'] > 0) && ($this->options['delta_limit'] != $cardinality)) || intval($this->options['delta_offset'])) {
+        $this->limit_values = TRUE;
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEntityManager() {
+    return $this->entityManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(AccountInterface $account) {
+    $access_control_handler = $this->entityManager->getAccessControlHandler($this->getEntityType());
+    return $access_control_handler->fieldAccess('view', $this->getFieldDefinition(), $account);
+  }
+
+  /**
+   * Called to add the field to a query.
+   *
+   * By default, all needed data is taken from entities loaded by the query
+   * plugin. Columns are added only if they are used in groupings.
+   */
+  public function query($use_groupby = FALSE) {
+    $fields = $this->additional_fields;
+    // No need to add the entity type.
+    $entity_type_key = array_search('entity_type', $fields);
+    if ($entity_type_key !== FALSE) {
+      unset($fields[$entity_type_key]);
+    }
+
+    if ($use_groupby) {
+      // Add the fields that we're actually grouping on.
+      $options = array();
+      if ($this->options['group_column'] != 'entity_id') {
+        $options = array($this->options['group_column'] => $this->options['group_column']);
+      }
+      $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : array();
+
+      // Go through the list and determine the actual column name from field api.
+      $fields = array();
+      $table_mapping = $this->getTableMapping();
+      $field_definition = $this->getFieldStorageDefinition();
+
+      foreach ($options as $column) {
+        $fields[$column] = $table_mapping->getFieldColumnName($field_definition, $column);
+      }
+
+      $this->group_fields = $fields;
+    }
+
+    // Add additional fields (and the table join itself) if needed.
+    if ($this->add_field_table($use_groupby)) {
+      $this->ensureMyTable();
+      $this->addAdditionalFields($fields);
+    }
+
+    // Let the entity field renderer alter the query if needed.
+    $this->getEntityFieldRenderer()->query($this->query, $this->relationship);
+  }
+
+  /**
+   * Determine if the field table should be added to the query.
+   */
+  function add_field_table($use_groupby) {
+    // Grouping is enabled.
+    if ($use_groupby) {
+      return TRUE;
+    }
+    // This a multiple value field, but "group multiple values" is not checked.
+    if ($this->multiple && !$this->options['group_rows']) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clickSortable() {
+    // A field is not click sortable if it's a multiple field with
+    // "group multiple values" checked, since a click sort in that case would
+    // add a join to the field table, which would produce unwanted duplicates.
+    if ($this->multiple && $this->options['group_rows']) {
+      return FALSE;
+    }
+
+    // If field definition is set, use that.
+    if (isset($this->definition['click sortable'])) {
+      return (bool) $this->definition['click sortable'];
+    }
+
+    // Default to true.
+    return TRUE;
+  }
+
+  /**
+   * Called to determine what to tell the clicksorter.
+   */
+  public function clickSort($order) {
+    // No column selected, can't continue.
+    if (empty($this->options['click_sort_column'])) {
+      return;
+    }
+
+    $this->ensureMyTable();
+    $field_storage_definition = $this->getFieldStorageDefinition();
+    $column = $this->getTableMapping()->getFieldColumnName($field_storage_definition, $this->options['click_sort_column']);
+    if (!isset($this->aliases[$column])) {
+      // Column is not in query; add a sort on it (without adding the column).
+      $this->aliases[$column] = $this->tableAlias . '.' . $column;
+    }
+    $this->query->addOrderBy(NULL, NULL, $order, $this->aliases[$column]);
+  }
+
+  /**
+   * Gets the field storage of the used field.
+   *
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
+   */
+  protected function getFieldStorageDefinition() {
+    $entity_type_id = $this->definition['entity_type'];
+    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
+
+    $field_storage = NULL;
+    // @todo Unify 'entity field'/'field_name' instead of converting back and
+    //   forth. https://www.drupal.org/node/2410779
+    if (isset($this->definition['field_name'])) {
+      $field_storage = $field_storage_definitions[$this->definition['field_name']];
+    }
+    elseif (isset($this->definition['entity field'])) {
+      $field_storage = $field_storage_definitions[$this->definition['entity field']];
+    }
+    return $field_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defineOptions() {
+    $options = parent::defineOptions();
+
+    $field_storage_definition = $this->getFieldStorageDefinition();
+    $field_type = $this->fieldTypePluginManager->getDefinition($field_storage_definition->getType());
+    $column_names = array_keys($field_storage_definition->getColumns());
+    $default_column = '';
+    // Try to determine a sensible default.
+    if (count($column_names) == 1) {
+      $default_column = $column_names[0];
+    }
+    elseif (in_array('value', $column_names)) {
+      $default_column = 'value';
+    }
+
+    // If the field has a "value" column, we probably need that one.
+    $options['click_sort_column'] = array(
+      'default' => $default_column,
+    );
+
+    if (isset($this->definition['default_formatter'])) {
+      $options['type'] = ['default' => $this->definition['default_formatter']];
+    }
+    elseif (isset($field_type['default_formatter'])) {
+      $options['type'] = ['default' => $field_type['default_formatter']];
+    }
+    else {
+      $options['type'] = ['default' => ''];
+    }
+
+    $options['settings'] = array(
+      'default' => isset($this->definition['default_formatter_settings']) ? $this->definition['default_formatter_settings'] : [],
+    );
+    $options['group_column'] = array(
+      'default' => $default_column,
+    );
+    $options['group_columns'] = array(
+      'default' => array(),
+    );
+
+    // Options used for multiple value fields.
+    $options['group_rows'] = array(
+      'default' => TRUE,
+    );
+    // If we know the exact number of allowed values, then that can be
+    // the default. Otherwise, default to 'all'.
+    $options['delta_limit'] = array(
+      'default' => ($field_storage_definition->getCardinality() > 1) ? $field_storage_definition->getCardinality() : 0,
+    );
+    $options['delta_offset'] = array(
+      'default' => 0,
+    );
+    $options['delta_reversed'] = array(
+      'default' => FALSE,
+    );
+    $options['delta_first_last'] = array(
+      'default' => FALSE,
+    );
+
+    $options['multi_type'] = array(
+      'default' => 'separator'
+    );
+    $options['separator'] = array(
+      'default' => ', '
+    );
+
+    $options['field_api_classes'] = array(
+      'default' => FALSE,
+    );
+
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
+    parent::buildOptionsForm($form, $form_state);
+
+    $field = $this->getFieldDefinition();
+    $formatters = $this->formatterPluginManager->getOptions($field->getType());
+    $column_names = array_keys($field->getColumns());
+
+    // If this is a multiple value field, add its options.
+    if ($this->multiple) {
+      $this->multiple_options_form($form, $form_state);
+    }
+
+    // No need to ask the user anything if the field has only one column.
+    if (count($field->getColumns()) == 1) {
+      $form['click_sort_column'] = array(
+        '#type' => 'value',
+        '#value' => isset($column_names[0]) ? $column_names[0] : '',
+      );
+    }
+    else {
+      $form['click_sort_column'] = array(
+        '#type' => 'select',
+        '#title' => $this->t('Column used for click sorting'),
+        '#options' => array_combine($column_names, $column_names),
+        '#default_value' => $this->options['click_sort_column'],
+        '#description' => $this->t('Used by Style: Table to determine the actual column to click sort the field on. The default is usually fine.'),
+      );
+    }
+
+    $form['type'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Formatter'),
+      '#options' => $formatters,
+      '#default_value' => $this->options['type'],
+      '#ajax' => array(
+        'url' => views_ui_build_form_url($form_state),
+      ),
+      '#submit' => array(array($this, 'submitTemporaryForm')),
+      '#executes_submit_callback' => TRUE,
+    );
+
+    $form['field_api_classes'] = array(
+      '#title' => $this->t('Use field template'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['field_api_classes'],
+      '#description' => $this->t('If checked, field api classes will be added by field templates. This is not recommended unless your CSS depends upon these classes. If not checked, template will not be used.'),
+      '#fieldset' => 'style_settings',
+      '#weight' => 20,
+    );
+
+    if ($this->multiple) {
+      $form['field_api_classes']['#description'] .= ' ' . $this->t('Checking this option will cause the group Display Type and Separator values to be ignored.');
+    }
+
+    // Get the settings form.
+    $settings_form = array('#value' => array());
+    $format = isset($form_state->getUserInput()['options']['type']) ? $form_state->getUserInput()['options']['type'] : $this->options['type'];
+    if ($formatter = $this->getFormatterInstance($format)) {
+      $settings_form = $formatter->settingsForm($form, $form_state);
+      // Convert field UI selector states to work in the Views field form.
+      FormHelper::rewriteStatesSelector($settings_form, "fields[{$field->getName()}][settings_edit_form]", 'options');
+    }
+    $form['settings'] = $settings_form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitFormCalculateOptions(array $options, array $form_state_options) {
+    // When we change the formatter type we don't want to keep any of the
+    // previous configured formatter settings, as there might be schema
+    // conflict.
+    unset($options['settings']);
+    $options = $form_state_options + $options;
+    if (!isset($options['settings'])) {
+      $options['settings'] = [];
+    }
+    return $options;
+  }
+
+  /**
+   * Provide options for multiple value fields.
+   */
+  function multiple_options_form(&$form, FormStateInterface $form_state) {
+    $field = $this->getFieldDefinition();
+
+    $form['multiple_field_settings'] = array(
+      '#type' => 'details',
+      '#title' => $this->t('Multiple field settings'),
+      '#weight' => 5,
+    );
+
+    $form['group_rows'] = array(
+      '#title' => $this->t('Display all values in the same row'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['group_rows'],
+      '#description' => $this->t('If checked, multiple values for this field will be shown in the same row. If not checked, each value in this field will create a new row. If using group by, please make sure to group by "Entity ID" for this setting to have any effect.'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    // Make the string translatable by keeping it as a whole rather than
+    // translating prefix and suffix separately.
+    list($prefix, $suffix) = explode('@count', $this->t('Display @count value(s)'));
+
+    if ($field->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
+      $type = 'textfield';
+      $options = NULL;
+      $size = 5;
+    }
+    else {
+      $type = 'select';
+      $range = range(1, $field->getCardinality());
+      $options = array_combine($range, $range);
+      $size = 1;
+    }
+    $form['multi_type'] = array(
+      '#type' => 'radios',
+      '#title' => $this->t('Display type'),
+      '#options' => array(
+        'ul' => $this->t('Unordered list'),
+        'ol' => $this->t('Ordered list'),
+        'separator' => $this->t('Simple separator'),
+      ),
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+        ),
+      ),
+      '#default_value' => $this->options['multi_type'],
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+          ':input[name="options[multi_type]"]' => array('value' => 'separator'),
+        ),
+      ),
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    $form['delta_limit'] = array(
+      '#type' => $type,
+      '#size' => $size,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#options' => $options,
+      '#default_value' => $this->options['delta_limit'],
+      '#prefix' => '<div class="container-inline">',
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+        ),
+      ),
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    list($prefix, $suffix) = explode('@count', $this->t('starting from @count'));
+    $form['delta_offset'] = array(
+      '#type' => 'textfield',
+      '#size' => 5,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#default_value' => $this->options['delta_offset'],
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+        ),
+      ),
+      '#description' => $this->t('(first item is 0)'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+    $form['delta_reversed'] = array(
+      '#title' => $this->t('Reversed'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['delta_reversed'],
+      '#suffix' => $suffix,
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+        ),
+      ),
+      '#description' => $this->t('(start from last values)'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+    $form['delta_first_last'] = array(
+      '#title' => $this->t('First and last only'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['delta_first_last'],
+      '#suffix' => '</div>',
+      '#states' => array(
+        'visible' => array(
+          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
+        ),
+      ),
+      '#fieldset' => 'multiple_field_settings',
+    );
+  }
+
+  /**
+   * Extend the groupby form with group columns.
+   */
+  public function buildGroupByForm(&$form, FormStateInterface $form_state) {
+    parent::buildGroupByForm($form, $form_state);
+    // With "field API" fields, the column target of the grouping function
+    // and any additional grouping columns must be specified.
+
+    $field_columns = array_keys($this->getFieldDefinition()->getColumns());
+    $group_columns = array(
+      'entity_id' => $this->t('Entity ID'),
+    ) + array_map('ucfirst', array_combine($field_columns, $field_columns));
+
+    $form['group_column'] = array(
+      '#type' => 'select',
+      '#title' => $this->t('Group column'),
+      '#default_value' => $this->options['group_column'],
+      '#description' => $this->t('Select the column of this field to apply the grouping function selected above.'),
+      '#options' => $group_columns,
+    );
+
+    $options = array(
+      'bundle' => 'Bundle',
+      'language' => 'Language',
+      'entity_type' => 'Entity_type',
+    );
+    // Add on defined fields, noting that they're prefixed with the field name.
+    $form['group_columns'] = array(
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Group columns (additional)'),
+      '#default_value' => $this->options['group_columns'],
+      '#description' => $this->t('Select any additional columns of this field to include in the query and to group on.'),
+      '#options' => $options + $group_columns,
+    );
+  }
+
+  public function submitGroupByForm(&$form, FormStateInterface $form_state) {
+    parent::submitGroupByForm($form, $form_state);
+    $item = &$form_state->get('handler')->options;
+
+    // Add settings for "field API" fields.
+    $item['group_column'] = $form_state->getValue(array('options', 'group_column'));
+    $item['group_columns'] = array_filter($form_state->getValue(array('options', 'group_columns')));
+  }
+
+  /**
+   * Render all items in this field together.
+   *
+   * When using advanced render, each possible item in the list is rendered
+   * individually. Then the items are all pasted together.
+   */
+  public function renderItems($items) {
+    if (!empty($items)) {
+      $items = $this->prepareItemsByDelta($items);
+      if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
+        $separator = $this->options['multi_type'] == 'separator' ? Xss::filterAdmin($this->options['separator']) : '';
+        $build = [
+          '#type' => 'inline_template',
+          '#template' => '{{ items | safe_join(separator) }}',
+          '#context' => ['separator' => $separator, 'items' => $items],
+        ];
+      }
+      else {
+        $build = array(
+          '#theme' => 'item_list',
+          '#items' => $items,
+          '#title' => NULL,
+          '#list_type' => $this->options['multi_type'],
+        );
+      }
+      return $this->renderer->render($build);
+    }
+  }
+
+  /**
+   * Adapts the $items according to the delta configuration.
+   *
+   * This selects displayed deltas, reorders items, and takes offsets into
+   * account.
+   *
+   * @param array $all_values
+   *   The items for individual rendering.
+   *
+   * @return array
+   *   The manipulated items.
+   */
+  protected function prepareItemsByDelta(array $all_values) {
+    if ($this->options['delta_reversed']) {
+      $all_values = array_reverse($all_values);
+    }
+
+    // We are supposed to show only certain deltas.
+    if ($this->limit_values) {
+      $row = $this->view->result[$this->view->row_index];
+
+      // Offset is calculated differently when row grouping for a field is not
+      // enabled. Since there are multiple rows, delta needs to be taken into
+      // account, so that different values are shown per row.
+      if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($row->{$this->aliases['delta']})) {
+        $delta_limit = 1;
+        $offset = $row->{$this->aliases['delta']};
+      }
+      // Single fields don't have a delta available so choose 0.
+      elseif (!$this->options['group_rows'] && !$this->multiple) {
+        $delta_limit = 1;
+        $offset = 0;
+      }
+      else {
+        $delta_limit = $this->options['delta_limit'];
+        $offset = intval($this->options['delta_offset']);
+
+        // We should only get here in this case if there is an offset, and in
+        // that case we are limiting to all values after the offset.
+        if ($delta_limit === 0) {
+          $delta_limit = count($all_values) - $offset;
+        }
+      }
+
+      // Determine if only the first and last values should be shown.
+      $delta_first_last = $this->options['delta_first_last'];
+
+      $new_values = array();
+      for ($i = 0; $i < $delta_limit; $i++) {
+        $new_delta = $offset + $i;
+
+        if (isset($all_values[$new_delta])) {
+          // If first-last option was selected, only use the first and last
+          // values.
+          if (!$delta_first_last
+            // Use the first value.
+            || $new_delta == $offset
+            // Use the last value.
+            || $new_delta == ($delta_limit + $offset - 1)) {
+            $new_values[] = $all_values[$new_delta];
+          }
+        }
+      }
+      $all_values = $new_values;
+    }
+
+    return $all_values;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preRender(&$values) {
+    parent::preRender($values);
+    $this->getEntityFieldRenderer()->preRender($values);
+  }
+
+  /**
+   * Returns the entity field renderer.
+   *
+   * @return \Drupal\views\Entity\Render\EntityFieldRenderer
+   *   The entity field renderer.
+   */
+  protected function getEntityFieldRenderer() {
+    if (!isset($this->entityFieldRenderer)) {
+      // This can be invoked during field handler initialization in which case
+      // view fields are not set yet.
+      if (!empty($this->view->field)) {
+        foreach ($this->view->field as $field) {
+          // An entity field renderer can handle only a single relationship.
+          if ($field->relationship == $this->relationship && isset($field->entityFieldRenderer)) {
+            $this->entityFieldRenderer = $field->entityFieldRenderer;
+            break;
+          }
+        }
+      }
+      if (!isset($this->entityFieldRenderer)) {
+        $entity_type = $this->entityManager->getDefinition($this->getEntityType());
+        $this->entityFieldRenderer = new EntityFieldRenderer($this->view, $this->relationship, $this->languageManager, $entity_type, $this->entityManager);
+      }
+    }
+    return $this->entityFieldRenderer;
+  }
+
+  /**
+   * Gets an array of items for the field.
+   *
+   * @param \Drupal\views\ResultRow $values
+   *   The result row object containing the values.
+   *
+   * @return array
+   *   An array of items for the field.
+   */
+  public function getItems(ResultRow $values) {
+    if (!$this->displayHandler->useGroupBy()) {
+      $build_list = $this->getEntityFieldRenderer()->render($values, $this);
+    }
+    else {
+      // For grouped results we need to retrieve a massaged entity having
+      // grouped field values to ensure that "grouped by" values, especially
+      // those with multiple cardinality work properly. See
+      // \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality.
+      $display = [
+        'type' => $this->options['type'],
+        'settings' => $this->options['settings'],
+        'label' => 'hidden',
+      ];
+      // Some bundles might not have a specific field, in which case the faked
+      // entity doesn't have it either.
+      $entity = $this->createEntityForGroupBy($this->getEntity($values), $values);
+      $build_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']}->view($display) : NULL;
+    }
+
+    if (!$build_list) {
+      return [];
+    }
+
+    if ($this->options['field_api_classes']) {
+      return [['rendered' => $this->renderer->render($build_list)]];
+    }
+
+    // Render using the formatted data itself.
+    $items = [];
+    // Each item is extracted and rendered separately, the top-level formatter
+    // render array itself is never rendered, so we extract its bubbleable
+    // metadata and add it to each child individually.
+    $bubbleable = BubbleableMetadata::createFromRenderArray($build_list);
+    foreach (Element::children($build_list) as $delta) {
+      BubbleableMetadata::createFromRenderArray($build_list[$delta])
+        ->merge($bubbleable)
+        ->applyTo($build_list[$delta]);
+      $items[$delta] = [
+        'rendered' => $build_list[$delta],
+        // Add the raw field items (for use in tokens).
+        'raw' => $build_list['#items'][$delta],
+      ];
+    }
+    return $items;
+  }
+
+  /**
+   * Creates a fake entity with grouped field values.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to be processed.
+   * @param \Drupal\views\ResultRow $row
+   *   The result row object containing the values.
+   *
+   * @return bool|\Drupal\Core\Entity\FieldableEntityInterface
+   *   Returns a new entity object containing the grouped field values.
+   */
+  protected function createEntityForGroupBy(EntityInterface $entity, ResultRow $row) {
+    // Retrieve the correct translation object.
+    $processed_entity = clone $this->getEntityFieldRenderer()->getEntityTranslation($entity, $row);
+
+    // Copy our group fields into the cloned entity. It is possible this will
+    // cause some weirdness, but there is only so much we can hope to do.
+    if (!empty($this->group_fields) && isset($entity->{$this->definition['field_name']})) {
+      // first, test to see if we have a base value.
+      $base_value = array();
+      // Note: We would copy original values here, but it can cause problems.
+      // For example, text fields store cached filtered values as 'safe_value'
+      // which does not appear anywhere in the field definition so we cannot
+      // affect it. Other side effects could happen similarly.
+      $data = FALSE;
+      foreach ($this->group_fields as $field_name => $column) {
+        if (property_exists($row, $this->aliases[$column])) {
+          $base_value[$field_name] = $row->{$this->aliases[$column]};
+          if (isset($base_value[$field_name])) {
+            $data = TRUE;
+          }
+        }
+      }
+
+      // If any of our aggregated fields have data, fake it:
+      if ($data) {
+        // Now, overwrite the original value with our aggregated value.
+        // This overwrites it so there is always just one entry.
+        $processed_entity->{$this->definition['field_name']} = array($base_value);
+      }
+      else {
+        $processed_entity->{$this->definition['field_name']} = array();
+      }
+    }
+
+    return $processed_entity;
+  }
+
+  function render_item($count, $item) {
+    return render($item['rendered']);
+  }
+
+  protected function documentSelfTokens(&$tokens) {
+    $field = $this->getFieldDefinition();
+    foreach ($field->getColumns() as $id => $column) {
+      $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = $this->t('Raw @column', array('@column' => $id));
+    }
+  }
+
+  protected function addSelfTokens(&$tokens, $item) {
+    $field = $this->getFieldDefinition();
+    foreach ($field->getColumns() as $id => $column) {
+      // Use \Drupal\Component\Utility\Xss::filterAdmin() because it's user data
+      // and we can't be sure it is safe. We know nothing about the data,
+      // though, so we can't really do much else.
+      if (isset($item['raw'])) {
+        $raw = $item['raw'];
+
+        if (is_array($raw)) {
+          if (isset($raw[$id]) && is_scalar($raw[$id])) {
+            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($raw[$id]);
+          }
+          else {
+            // Make sure that empty values are replaced as well.
+            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
+          }
+        }
+
+        if (is_object($raw)) {
+          $property = $raw->get($id);
+          // Check if TypedDataInterface is implemented so we know how to render
+          // the item as a string.
+          if (!empty($property) && $property instanceof TypedDataInterface) {
+            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($property->getString());
+          }
+          else {
+            // Make sure that empty values are replaced as well.
+            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Returns the field formatter instance.
+   *
+   * @return \Drupal\Core\Field\FormatterInterface|null
+   *   The field formatter instance.
+   */
+  protected function getFormatterInstance($format = NULL) {
+    if (!isset($format)) {
+      $format = $this->options['type'];
+    }
+    $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
+
+    $options = [
+      'field_definition' => $this->getFieldDefinition(),
+      'configuration' => [
+        'type' => $format,
+        'settings' => $settings,
+        'label' => '',
+        'weight' => 0,
+      ],
+      'view_mode' => '_custom',
+    ];
+
+    return $this->formatterPluginManager->getInstance($options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    $this->dependencies = parent::calculateDependencies();
+
+    // Add the module providing the configured field storage as a dependency.
+    if (($field_storage_definition = $this->getFieldStorageDefinition()) && $field_storage_definition instanceof EntityInterface) {
+      $this->dependencies['config'][] = $field_storage_definition->getConfigDependencyName();
+    }
+    if (!empty($this->options['type'])) {
+      // Add the module providing the formatter.
+      $this->dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
+
+      // Add the formatter's dependencies.
+      if (($formatter = $this->getFormatterInstance()) && $formatter instanceof DependentPluginInterface) {
+        $this->calculatePluginDependencies($formatter);
+      }
+    }
+
+    return $this->dependencies;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    return $this->getEntityFieldRenderer()->getCacheContexts();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    $field_definition = $this->getFieldDefinition();
+    $field_storage_definition = $this->getFieldStorageDefinition();
+    return Cache::mergeTags(
+      $field_definition instanceof CacheableDependencyInterface ? $field_definition->getCacheTags() : [],
+      $field_storage_definition instanceof CacheableDependencyInterface ? $field_storage_definition->getCacheTags() : []
+    );
+  }
+
+  /**
+   * Gets the table mapping for the entity type of the field.
+   *
+   * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
+   *   The table mapping.
+   */
+  protected function getTableMapping() {
+    return $this->entityManager->getStorage($this->definition['entity_type'])->getTableMapping();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue(ResultRow $values, $field = NULL) {
+    $entity = $this->getEntity($values);
+    // Some bundles might not have a specific field, in which case the entity
+    // (potentially a fake one) doesn't have it either.
+    /** @var \Drupal\Core\Field\FieldItemListInterface $field_item_list */
+    $field_item_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']} : NULL;
+
+    if (!isset($field_item_list)) {
+      // There isn't anything we can do without a valid field.
+      return NULL;
+    }
+
+    $field_item_definition = $field_item_list->getFieldDefinition();
+
+    $values = [];
+    foreach ($field_item_list as $field_item) {
+      /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
+      if ($field) {
+        $values[] = $field_item->$field;
+      }
+      // Find the value using the main property of the field. If no main
+      // property is provided fall back to 'value'.
+      elseif ($main_property_name = $field_item->mainPropertyName()) {
+        $values[] = $field_item->{$main_property_name};
+      }
+      else {
+        $values[] = $field_item->value;
+      }
+    }
+    if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
+      return reset($values);
+    }
+    else {
+      return $values;
+    }
+  }
+
+}
diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index c4003b4e4225..625d3c8aced7 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -2,1074 +2,12 @@
 
 namespace Drupal\views\Plugin\views\field;
 
-use Drupal\Component\Plugin\DependentPluginInterface;
-use Drupal\Component\Utility\Xss;
-use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheableDependencyInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
-use Drupal\Core\Field\FieldTypePluginManagerInterface;
-use Drupal\Core\Field\FormatterPluginManager;
-use Drupal\Core\Form\FormHelper;
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\Plugin\PluginDependencyTrait;
-use Drupal\Core\Render\BubbleableMetadata;
-use Drupal\Core\Render\Element;
-use Drupal\Core\Render\RendererInterface;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\views\FieldAPIHandlerTrait;
-use Drupal\views\Entity\Render\EntityFieldRenderer;
-use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\ResultRow;
-use Drupal\views\ViewExecutable;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-
 /**
- * A field that displays entity field data.
- *
- * @ingroup views_field_handlers
+ * A stub class to provide backward compatibility for EntityField.
  *
- * @todo Rename the class https://www.drupal.org/node/2408667
- *
- * @ViewsField("field")
+ * @deprecated in Drupal 8.3.x and will be removed before 9.0.0
+ *   Use \Drupal\views\Plugin\views\field\EntityField instead.
  */
-class Field extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface {
-
-  use FieldAPIHandlerTrait;
-  use PluginDependencyTrait;
-
-  /**
-   * An array to store field renderable arrays for use by renderItems().
-   *
-   * @var array
-   */
-  public $items = array();
-
-  /**
-   * Does the field supports multiple field values.
-   *
-   * @var bool
-   */
-  public $multiple;
-
-  /**
-   * Does the rendered fields get limited.
-   *
-   * @var bool
-   */
-  public $limit_values;
-
-  /**
-   * A shortcut for $view->base_table.
-   *
-   * @var string
-   */
-  public $base_table;
-
-  /**
-   * An array of formatter options.
-   *
-   * @var array
-   */
-  protected $formatterOptions;
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The field formatter plugin manager.
-   *
-   * @var \Drupal\Core\Field\FormatterPluginManager
-   */
-  protected $formatterPluginManager;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * The renderer.
-   *
-   * @var \Drupal\Core\Render\RendererInterface
-   */
-  protected $renderer;
-
-  /**
-   * The field type plugin manager.
-   *
-   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
-   */
-  protected $fieldTypePluginManager;
-
-  /**
-   * Static cache for ::getEntityFieldRenderer().
-   *
-   * @var \Drupal\views\Entity\Render\EntityFieldRenderer
-   */
-  protected $entityFieldRenderer;
-
-  /**
-   * Constructs a \Drupal\field\Plugin\views\field\Field object.
-   *
-   * @param array $configuration
-   *   A configuration array containing information about the plugin instance.
-   * @param string $plugin_id
-   *   The plugin_id for the plugin instance.
-   * @param mixed $plugin_definition
-   *   The plugin implementation definition.
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The field formatter plugin manager.
-   * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
-   *   The field formatter plugin manager.
-   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_plugin_manager
-   *   The field plugin type manager.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   * @param \Drupal\Core\Render\RendererInterface $renderer
-   *   The renderer.
-   */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager, RendererInterface $renderer) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition);
-
-    $this->entityManager = $entity_manager;
-    $this->formatterPluginManager = $formatter_plugin_manager;
-    $this->fieldTypePluginManager = $field_type_plugin_manager;
-    $this->languageManager = $language_manager;
-    $this->renderer = $renderer;
-
-    // @todo Unify 'entity field'/'field_name' instead of converting back and
-    //   forth. https://www.drupal.org/node/2410779
-    if (isset($this->definition['entity field'])) {
-      $this->definition['field_name'] = $this->definition['entity field'];
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static(
-      $configuration,
-      $plugin_id,
-      $plugin_definition,
-      $container->get('entity.manager'),
-      $container->get('plugin.manager.field.formatter'),
-      $container->get('plugin.manager.field.field_type'),
-      $container->get('language_manager'),
-      $container->get('renderer')
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
-    parent::init($view, $display, $options);
-
-    $this->multiple = FALSE;
-    $this->limit_values = FALSE;
-
-    $field_definition = $this->getFieldDefinition();
-    $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality();
-    if ($field_definition->getFieldStorageDefinition()->isMultiple()) {
-      $this->multiple = TRUE;
-
-      // If "Display all values in the same row" is FALSE, then we always limit
-      // in order to show a single unique value per row.
-      if (!$this->options['group_rows']) {
-        $this->limit_values = TRUE;
-      }
-
-      // If "First and last only" is chosen, limit the values
-      if (!empty($this->options['delta_first_last'])) {
-        $this->limit_values = TRUE;
-      }
-
-      // Otherwise, we only limit values if the user hasn't selected "all", 0, or
-      // the value matching field cardinality.
-      if ((($this->options['delta_limit'] > 0) && ($this->options['delta_limit'] != $cardinality)) || intval($this->options['delta_offset'])) {
-        $this->limit_values = TRUE;
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getEntityManager() {
-    return $this->entityManager;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(AccountInterface $account) {
-    $access_control_handler = $this->entityManager->getAccessControlHandler($this->getEntityType());
-    return $access_control_handler->fieldAccess('view', $this->getFieldDefinition(), $account);
-  }
-
-  /**
-   * Called to add the field to a query.
-   *
-   * By default, all needed data is taken from entities loaded by the query
-   * plugin. Columns are added only if they are used in groupings.
-   */
-  public function query($use_groupby = FALSE) {
-    $fields = $this->additional_fields;
-    // No need to add the entity type.
-    $entity_type_key = array_search('entity_type', $fields);
-    if ($entity_type_key !== FALSE) {
-      unset($fields[$entity_type_key]);
-    }
-
-    if ($use_groupby) {
-      // Add the fields that we're actually grouping on.
-      $options = array();
-      if ($this->options['group_column'] != 'entity_id') {
-        $options = array($this->options['group_column'] => $this->options['group_column']);
-      }
-      $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : array();
-
-      // Go through the list and determine the actual column name from field api.
-      $fields = array();
-      $table_mapping = $this->getTableMapping();
-      $field_definition = $this->getFieldStorageDefinition();
-
-      foreach ($options as $column) {
-        $fields[$column] = $table_mapping->getFieldColumnName($field_definition, $column);
-      }
-
-      $this->group_fields = $fields;
-    }
-
-    // Add additional fields (and the table join itself) if needed.
-    if ($this->add_field_table($use_groupby)) {
-      $this->ensureMyTable();
-      $this->addAdditionalFields($fields);
-    }
-
-    // Let the entity field renderer alter the query if needed.
-    $this->getEntityFieldRenderer()->query($this->query, $this->relationship);
-  }
-
-  /**
-   * Determine if the field table should be added to the query.
-   */
-  function add_field_table($use_groupby) {
-    // Grouping is enabled.
-    if ($use_groupby) {
-      return TRUE;
-    }
-    // This a multiple value field, but "group multiple values" is not checked.
-    if ($this->multiple && !$this->options['group_rows']) {
-      return TRUE;
-    }
-    return FALSE;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function clickSortable() {
-    // A field is not click sortable if it's a multiple field with
-    // "group multiple values" checked, since a click sort in that case would
-    // add a join to the field table, which would produce unwanted duplicates.
-    if ($this->multiple && $this->options['group_rows']) {
-      return FALSE;
-    }
-
-    // If field definition is set, use that.
-    if (isset($this->definition['click sortable'])) {
-      return (bool) $this->definition['click sortable'];
-    }
-
-    // Default to true.
-    return TRUE;
-  }
-
-  /**
-   * Called to determine what to tell the clicksorter.
-   */
-  public function clickSort($order) {
-    // No column selected, can't continue.
-    if (empty($this->options['click_sort_column'])) {
-      return;
-    }
-
-    $this->ensureMyTable();
-    $field_storage_definition = $this->getFieldStorageDefinition();
-    $column = $this->getTableMapping()->getFieldColumnName($field_storage_definition, $this->options['click_sort_column']);
-    if (!isset($this->aliases[$column])) {
-      // Column is not in query; add a sort on it (without adding the column).
-      $this->aliases[$column] = $this->tableAlias . '.' . $column;
-    }
-    $this->query->addOrderBy(NULL, NULL, $order, $this->aliases[$column]);
-  }
-
-  /**
-   * Gets the field storage of the used field.
-   *
-   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
-   */
-  protected function getFieldStorageDefinition() {
-    $entity_type_id = $this->definition['entity_type'];
-    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
-
-    $field_storage = NULL;
-    // @todo Unify 'entity field'/'field_name' instead of converting back and
-    //   forth. https://www.drupal.org/node/2410779
-    if (isset($this->definition['field_name'])) {
-      $field_storage = $field_storage_definitions[$this->definition['field_name']];
-    }
-    elseif (isset($this->definition['entity field'])) {
-      $field_storage = $field_storage_definitions[$this->definition['entity field']];
-    }
-    return $field_storage;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function defineOptions() {
-    $options = parent::defineOptions();
-
-    $field_storage_definition = $this->getFieldStorageDefinition();
-    $field_type = $this->fieldTypePluginManager->getDefinition($field_storage_definition->getType());
-    $column_names = array_keys($field_storage_definition->getColumns());
-    $default_column = '';
-    // Try to determine a sensible default.
-    if (count($column_names) == 1) {
-      $default_column = $column_names[0];
-    }
-    elseif (in_array('value', $column_names)) {
-      $default_column = 'value';
-    }
-
-    // If the field has a "value" column, we probably need that one.
-    $options['click_sort_column'] = array(
-      'default' => $default_column,
-    );
-
-    if (isset($this->definition['default_formatter'])) {
-      $options['type'] = ['default' => $this->definition['default_formatter']];
-    }
-    elseif (isset($field_type['default_formatter'])) {
-      $options['type'] = ['default' => $field_type['default_formatter']];
-    }
-    else {
-      $options['type'] = ['default' => ''];
-    }
-
-    $options['settings'] = array(
-      'default' => isset($this->definition['default_formatter_settings']) ? $this->definition['default_formatter_settings'] : [],
-    );
-    $options['group_column'] = array(
-      'default' => $default_column,
-    );
-    $options['group_columns'] = array(
-      'default' => array(),
-    );
-
-    // Options used for multiple value fields.
-    $options['group_rows'] = array(
-      'default' => TRUE,
-    );
-    // If we know the exact number of allowed values, then that can be
-    // the default. Otherwise, default to 'all'.
-    $options['delta_limit'] = array(
-      'default' => ($field_storage_definition->getCardinality() > 1) ? $field_storage_definition->getCardinality() : 0,
-    );
-    $options['delta_offset'] = array(
-      'default' => 0,
-    );
-    $options['delta_reversed'] = array(
-      'default' => FALSE,
-    );
-    $options['delta_first_last'] = array(
-      'default' => FALSE,
-    );
-
-    $options['multi_type'] = array(
-      'default' => 'separator'
-    );
-    $options['separator'] = array(
-      'default' => ', '
-    );
-
-    $options['field_api_classes'] = array(
-      'default' => FALSE,
-    );
-
-    return $options;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
-    parent::buildOptionsForm($form, $form_state);
-
-    $field = $this->getFieldDefinition();
-    $formatters = $this->formatterPluginManager->getOptions($field->getType());
-    $column_names = array_keys($field->getColumns());
-
-    // If this is a multiple value field, add its options.
-    if ($this->multiple) {
-      $this->multiple_options_form($form, $form_state);
-    }
-
-    // No need to ask the user anything if the field has only one column.
-    if (count($field->getColumns()) == 1) {
-      $form['click_sort_column'] = array(
-        '#type' => 'value',
-        '#value' => isset($column_names[0]) ? $column_names[0] : '',
-      );
-    }
-    else {
-      $form['click_sort_column'] = array(
-        '#type' => 'select',
-        '#title' => $this->t('Column used for click sorting'),
-        '#options' => array_combine($column_names, $column_names),
-        '#default_value' => $this->options['click_sort_column'],
-        '#description' => $this->t('Used by Style: Table to determine the actual column to click sort the field on. The default is usually fine.'),
-      );
-    }
-
-    $form['type'] = array(
-      '#type' => 'select',
-      '#title' => $this->t('Formatter'),
-      '#options' => $formatters,
-      '#default_value' => $this->options['type'],
-      '#ajax' => array(
-        'url' => views_ui_build_form_url($form_state),
-      ),
-      '#submit' => array(array($this, 'submitTemporaryForm')),
-      '#executes_submit_callback' => TRUE,
-    );
-
-    $form['field_api_classes'] = array(
-      '#title' => $this->t('Use field template'),
-      '#type' => 'checkbox',
-      '#default_value' => $this->options['field_api_classes'],
-      '#description' => $this->t('If checked, field api classes will be added by field templates. This is not recommended unless your CSS depends upon these classes. If not checked, template will not be used.'),
-      '#fieldset' => 'style_settings',
-      '#weight' => 20,
-    );
-
-    if ($this->multiple) {
-      $form['field_api_classes']['#description'] .= ' ' . $this->t('Checking this option will cause the group Display Type and Separator values to be ignored.');
-    }
-
-    // Get the settings form.
-    $settings_form = array('#value' => array());
-    $format = isset($form_state->getUserInput()['options']['type']) ? $form_state->getUserInput()['options']['type'] : $this->options['type'];
-    if ($formatter = $this->getFormatterInstance($format)) {
-      $settings_form = $formatter->settingsForm($form, $form_state);
-      // Convert field UI selector states to work in the Views field form.
-      FormHelper::rewriteStatesSelector($settings_form, "fields[{$field->getName()}][settings_edit_form]", 'options');
-    }
-    $form['settings'] = $settings_form;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitFormCalculateOptions(array $options, array $form_state_options) {
-    // When we change the formatter type we don't want to keep any of the
-    // previous configured formatter settings, as there might be schema
-    // conflict.
-    unset($options['settings']);
-    $options = $form_state_options + $options;
-    if (!isset($options['settings'])) {
-      $options['settings'] = [];
-    }
-    return $options;
-  }
-
-  /**
-   * Provide options for multiple value fields.
-   */
-  function multiple_options_form(&$form, FormStateInterface $form_state) {
-    $field = $this->getFieldDefinition();
-
-    $form['multiple_field_settings'] = array(
-      '#type' => 'details',
-      '#title' => $this->t('Multiple field settings'),
-      '#weight' => 5,
-    );
-
-    $form['group_rows'] = array(
-      '#title' => $this->t('Display all values in the same row'),
-      '#type' => 'checkbox',
-      '#default_value' => $this->options['group_rows'],
-      '#description' => $this->t('If checked, multiple values for this field will be shown in the same row. If not checked, each value in this field will create a new row. If using group by, please make sure to group by "Entity ID" for this setting to have any effect.'),
-      '#fieldset' => 'multiple_field_settings',
-    );
-
-    // Make the string translatable by keeping it as a whole rather than
-    // translating prefix and suffix separately.
-    list($prefix, $suffix) = explode('@count', $this->t('Display @count value(s)'));
-
-    if ($field->getCardinality() == FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
-      $type = 'textfield';
-      $options = NULL;
-      $size = 5;
-    }
-    else {
-      $type = 'select';
-      $range = range(1, $field->getCardinality());
-      $options = array_combine($range, $range);
-      $size = 1;
-    }
-    $form['multi_type'] = array(
-      '#type' => 'radios',
-      '#title' => $this->t('Display type'),
-      '#options' => array(
-        'ul' => $this->t('Unordered list'),
-        'ol' => $this->t('Ordered list'),
-        'separator' => $this->t('Simple separator'),
-      ),
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#default_value' => $this->options['multi_type'],
-      '#fieldset' => 'multiple_field_settings',
-    );
-
-    $form['separator'] = array(
-      '#type' => 'textfield',
-      '#title' => $this->t('Separator'),
-      '#default_value' => $this->options['separator'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-          ':input[name="options[multi_type]"]' => array('value' => 'separator'),
-        ),
-      ),
-      '#fieldset' => 'multiple_field_settings',
-    );
-
-    $form['delta_limit'] = array(
-      '#type' => $type,
-      '#size' => $size,
-      '#field_prefix' => $prefix,
-      '#field_suffix' => $suffix,
-      '#options' => $options,
-      '#default_value' => $this->options['delta_limit'],
-      '#prefix' => '<div class="container-inline">',
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#fieldset' => 'multiple_field_settings',
-    );
-
-    list($prefix, $suffix) = explode('@count', $this->t('starting from @count'));
-    $form['delta_offset'] = array(
-      '#type' => 'textfield',
-      '#size' => 5,
-      '#field_prefix' => $prefix,
-      '#field_suffix' => $suffix,
-      '#default_value' => $this->options['delta_offset'],
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#description' => $this->t('(first item is 0)'),
-      '#fieldset' => 'multiple_field_settings',
-    );
-    $form['delta_reversed'] = array(
-      '#title' => $this->t('Reversed'),
-      '#type' => 'checkbox',
-      '#default_value' => $this->options['delta_reversed'],
-      '#suffix' => $suffix,
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#description' => $this->t('(start from last values)'),
-      '#fieldset' => 'multiple_field_settings',
-    );
-    $form['delta_first_last'] = array(
-      '#title' => $this->t('First and last only'),
-      '#type' => 'checkbox',
-      '#default_value' => $this->options['delta_first_last'],
-      '#suffix' => '</div>',
-      '#states' => array(
-        'visible' => array(
-          ':input[name="options[group_rows]"]' => array('checked' => TRUE),
-        ),
-      ),
-      '#fieldset' => 'multiple_field_settings',
-    );
-  }
-
-  /**
-   * Extend the groupby form with group columns.
-   */
-  public function buildGroupByForm(&$form, FormStateInterface $form_state) {
-    parent::buildGroupByForm($form, $form_state);
-    // With "field API" fields, the column target of the grouping function
-    // and any additional grouping columns must be specified.
-
-    $field_columns = array_keys($this->getFieldDefinition()->getColumns());
-    $group_columns = array(
-      'entity_id' => $this->t('Entity ID'),
-    ) + array_map('ucfirst', array_combine($field_columns, $field_columns));
-
-    $form['group_column'] = array(
-      '#type' => 'select',
-      '#title' => $this->t('Group column'),
-      '#default_value' => $this->options['group_column'],
-      '#description' => $this->t('Select the column of this field to apply the grouping function selected above.'),
-      '#options' => $group_columns,
-    );
-
-    $options = array(
-      'bundle' => 'Bundle',
-      'language' => 'Language',
-      'entity_type' => 'Entity_type',
-    );
-    // Add on defined fields, noting that they're prefixed with the field name.
-    $form['group_columns'] = array(
-      '#type' => 'checkboxes',
-      '#title' => $this->t('Group columns (additional)'),
-      '#default_value' => $this->options['group_columns'],
-      '#description' => $this->t('Select any additional columns of this field to include in the query and to group on.'),
-      '#options' => $options + $group_columns,
-    );
-  }
-
-  public function submitGroupByForm(&$form, FormStateInterface $form_state) {
-    parent::submitGroupByForm($form, $form_state);
-    $item = &$form_state->get('handler')->options;
-
-    // Add settings for "field API" fields.
-    $item['group_column'] = $form_state->getValue(array('options', 'group_column'));
-    $item['group_columns'] = array_filter($form_state->getValue(array('options', 'group_columns')));
-  }
-
-  /**
-   * Render all items in this field together.
-   *
-   * When using advanced render, each possible item in the list is rendered
-   * individually. Then the items are all pasted together.
-   */
-  public function renderItems($items) {
-    if (!empty($items)) {
-      $items = $this->prepareItemsByDelta($items);
-      if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
-        $separator = $this->options['multi_type'] == 'separator' ? Xss::filterAdmin($this->options['separator']) : '';
-        $build = [
-          '#type' => 'inline_template',
-          '#template' => '{{ items | safe_join(separator) }}',
-          '#context' => ['separator' => $separator, 'items' => $items],
-        ];
-      }
-      else {
-        $build = array(
-          '#theme' => 'item_list',
-          '#items' => $items,
-          '#title' => NULL,
-          '#list_type' => $this->options['multi_type'],
-        );
-      }
-      return $this->renderer->render($build);
-    }
-  }
-
-  /**
-   * Adapts the $items according to the delta configuration.
-   *
-   * This selects displayed deltas, reorders items, and takes offsets into
-   * account.
-   *
-   * @param array $all_values
-   *   The items for individual rendering.
-   *
-   * @return array
-   *   The manipulated items.
-   */
-  protected function prepareItemsByDelta(array $all_values) {
-    if ($this->options['delta_reversed']) {
-      $all_values = array_reverse($all_values);
-    }
-
-    // We are supposed to show only certain deltas.
-    if ($this->limit_values) {
-      $row = $this->view->result[$this->view->row_index];
-
-      // Offset is calculated differently when row grouping for a field is not
-      // enabled. Since there are multiple rows, delta needs to be taken into
-      // account, so that different values are shown per row.
-      if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($row->{$this->aliases['delta']})) {
-        $delta_limit = 1;
-        $offset = $row->{$this->aliases['delta']};
-      }
-      // Single fields don't have a delta available so choose 0.
-      elseif (!$this->options['group_rows'] && !$this->multiple) {
-        $delta_limit = 1;
-        $offset = 0;
-      }
-      else {
-        $delta_limit = $this->options['delta_limit'];
-        $offset = intval($this->options['delta_offset']);
-
-        // We should only get here in this case if there is an offset, and in
-        // that case we are limiting to all values after the offset.
-        if ($delta_limit === 0) {
-          $delta_limit = count($all_values) - $offset;
-        }
-      }
-
-      // Determine if only the first and last values should be shown.
-      $delta_first_last = $this->options['delta_first_last'];
-
-      $new_values = array();
-      for ($i = 0; $i < $delta_limit; $i++) {
-        $new_delta = $offset + $i;
-
-        if (isset($all_values[$new_delta])) {
-          // If first-last option was selected, only use the first and last
-          // values.
-          if (!$delta_first_last
-            // Use the first value.
-            || $new_delta == $offset
-            // Use the last value.
-            || $new_delta == ($delta_limit + $offset - 1)) {
-            $new_values[] = $all_values[$new_delta];
-          }
-        }
-      }
-      $all_values = $new_values;
-    }
-
-    return $all_values;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function preRender(&$values) {
-    parent::preRender($values);
-    $this->getEntityFieldRenderer()->preRender($values);
-  }
-
-  /**
-   * Returns the entity field renderer.
-   *
-   * @return \Drupal\views\Entity\Render\EntityFieldRenderer
-   *   The entity field renderer.
-   */
-  protected function getEntityFieldRenderer() {
-    if (!isset($this->entityFieldRenderer)) {
-      // This can be invoked during field handler initialization in which case
-      // view fields are not set yet.
-      if (!empty($this->view->field)) {
-        foreach ($this->view->field as $field) {
-          // An entity field renderer can handle only a single relationship.
-          if ($field->relationship == $this->relationship && isset($field->entityFieldRenderer)) {
-            $this->entityFieldRenderer = $field->entityFieldRenderer;
-            break;
-          }
-        }
-      }
-      if (!isset($this->entityFieldRenderer)) {
-        $entity_type = $this->entityManager->getDefinition($this->getEntityType());
-        $this->entityFieldRenderer = new EntityFieldRenderer($this->view, $this->relationship, $this->languageManager, $entity_type, $this->entityManager);
-      }
-    }
-    return $this->entityFieldRenderer;
-  }
-
-  /**
-   * Gets an array of items for the field.
-   *
-   * @param \Drupal\views\ResultRow $values
-   *   The result row object containing the values.
-   *
-   * @return array
-   *   An array of items for the field.
-   */
-  public function getItems(ResultRow $values) {
-    if (!$this->displayHandler->useGroupBy()) {
-      $build_list = $this->getEntityFieldRenderer()->render($values, $this);
-    }
-    else {
-      // For grouped results we need to retrieve a massaged entity having
-      // grouped field values to ensure that "grouped by" values, especially
-      // those with multiple cardinality work properly. See
-      // \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality.
-      $display = [
-        'type' => $this->options['type'],
-        'settings' => $this->options['settings'],
-        'label' => 'hidden',
-      ];
-      // Some bundles might not have a specific field, in which case the faked
-      // entity doesn't have it either.
-      $entity = $this->createEntityForGroupBy($this->getEntity($values), $values);
-      $build_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']}->view($display) : NULL;
-    }
-
-    if (!$build_list) {
-      return [];
-    }
-
-    if ($this->options['field_api_classes']) {
-      return [['rendered' => $this->renderer->render($build_list)]];
-    }
-
-    // Render using the formatted data itself.
-    $items = [];
-    // Each item is extracted and rendered separately, the top-level formatter
-    // render array itself is never rendered, so we extract its bubbleable
-    // metadata and add it to each child individually.
-    $bubbleable = BubbleableMetadata::createFromRenderArray($build_list);
-    foreach (Element::children($build_list) as $delta) {
-      BubbleableMetadata::createFromRenderArray($build_list[$delta])
-        ->merge($bubbleable)
-        ->applyTo($build_list[$delta]);
-      $items[$delta] = [
-        'rendered' => $build_list[$delta],
-        // Add the raw field items (for use in tokens).
-        'raw' => $build_list['#items'][$delta],
-      ];
-    }
-    return $items;
-  }
-
-  /**
-   * Creates a fake entity with grouped field values.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to be processed.
-   * @param \Drupal\views\ResultRow $row
-   *   The result row object containing the values.
-   *
-   * @return bool|\Drupal\Core\Entity\FieldableEntityInterface
-   *   Returns a new entity object containing the grouped field values.
-   */
-  protected function createEntityForGroupBy(EntityInterface $entity, ResultRow $row) {
-    // Retrieve the correct translation object.
-    $processed_entity = clone $this->getEntityFieldRenderer()->getEntityTranslation($entity, $row);
-
-    // Copy our group fields into the cloned entity. It is possible this will
-    // cause some weirdness, but there is only so much we can hope to do.
-    if (!empty($this->group_fields) && isset($entity->{$this->definition['field_name']})) {
-      // first, test to see if we have a base value.
-      $base_value = array();
-      // Note: We would copy original values here, but it can cause problems.
-      // For example, text fields store cached filtered values as 'safe_value'
-      // which does not appear anywhere in the field definition so we cannot
-      // affect it. Other side effects could happen similarly.
-      $data = FALSE;
-      foreach ($this->group_fields as $field_name => $column) {
-        if (property_exists($row, $this->aliases[$column])) {
-          $base_value[$field_name] = $row->{$this->aliases[$column]};
-          if (isset($base_value[$field_name])) {
-            $data = TRUE;
-          }
-        }
-      }
-
-      // If any of our aggregated fields have data, fake it:
-      if ($data) {
-        // Now, overwrite the original value with our aggregated value.
-        // This overwrites it so there is always just one entry.
-        $processed_entity->{$this->definition['field_name']} = array($base_value);
-      }
-      else {
-        $processed_entity->{$this->definition['field_name']} = array();
-      }
-    }
-
-    return $processed_entity;
-  }
-
-  function render_item($count, $item) {
-    return render($item['rendered']);
-  }
-
-  protected function documentSelfTokens(&$tokens) {
-    $field = $this->getFieldDefinition();
-    foreach ($field->getColumns() as $id => $column) {
-      $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = $this->t('Raw @column', array('@column' => $id));
-    }
-  }
-
-  protected function addSelfTokens(&$tokens, $item) {
-    $field = $this->getFieldDefinition();
-    foreach ($field->getColumns() as $id => $column) {
-      // Use \Drupal\Component\Utility\Xss::filterAdmin() because it's user data
-      // and we can't be sure it is safe. We know nothing about the data,
-      // though, so we can't really do much else.
-      if (isset($item['raw'])) {
-        $raw = $item['raw'];
-
-        if (is_array($raw)) {
-          if (isset($raw[$id]) && is_scalar($raw[$id])) {
-            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($raw[$id]);
-          }
-          else {
-            // Make sure that empty values are replaced as well.
-            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
-          }
-        }
-
-        if (is_object($raw)) {
-          $property = $raw->get($id);
-          // Check if TypedDataInterface is implemented so we know how to render
-          // the item as a string.
-          if (!empty($property) && $property instanceof TypedDataInterface) {
-            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = Xss::filterAdmin($property->getString());
-          }
-          else {
-            // Make sure that empty values are replaced as well.
-            $tokens['{{ ' . $this->options['id'] . '__' . $id . ' }}'] = '';
-          }
-        }
-      }
-    }
-  }
-
-  /**
-   * Returns the field formatter instance.
-   *
-   * @return \Drupal\Core\Field\FormatterInterface|null
-   *   The field formatter instance.
-   */
-  protected function getFormatterInstance($format = NULL) {
-    if (!isset($format)) {
-      $format = $this->options['type'];
-    }
-    $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
-
-    $options = [
-      'field_definition' => $this->getFieldDefinition(),
-      'configuration' => [
-        'type' => $format,
-        'settings' => $settings,
-        'label' => '',
-        'weight' => 0,
-      ],
-      'view_mode' => '_custom',
-    ];
-
-    return $this->formatterPluginManager->getInstance($options);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function calculateDependencies() {
-    $this->dependencies = parent::calculateDependencies();
-
-    // Add the module providing the configured field storage as a dependency.
-    if (($field_storage_definition = $this->getFieldStorageDefinition()) && $field_storage_definition instanceof EntityInterface) {
-      $this->dependencies['config'][] = $field_storage_definition->getConfigDependencyName();
-    }
-    if (!empty($this->options['type'])) {
-      // Add the module providing the formatter.
-      $this->dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
-
-      // Add the formatter's dependencies.
-      if (($formatter = $this->getFormatterInstance()) && $formatter instanceof DependentPluginInterface) {
-        $this->calculatePluginDependencies($formatter);
-      }
-    }
-
-    return $this->dependencies;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCacheMaxAge() {
-    return Cache::PERMANENT;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCacheContexts() {
-    return $this->getEntityFieldRenderer()->getCacheContexts();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCacheTags() {
-    $field_definition = $this->getFieldDefinition();
-    $field_storage_definition = $this->getFieldStorageDefinition();
-    return Cache::mergeTags(
-      $field_definition instanceof CacheableDependencyInterface ? $field_definition->getCacheTags() : [],
-      $field_storage_definition instanceof CacheableDependencyInterface ? $field_storage_definition->getCacheTags() : []
-    );
-  }
-
-  /**
-   * Gets the table mapping for the entity type of the field.
-   *
-   * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
-   *   The table mapping.
-   */
-  protected function getTableMapping() {
-    return $this->entityManager->getStorage($this->definition['entity_type'])->getTableMapping();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getValue(ResultRow $values, $field = NULL) {
-    $entity = $this->getEntity($values);
-    // Some bundles might not have a specific field, in which case the entity
-    // (potentially a fake one) doesn't have it either.
-    /** @var \Drupal\Core\Field\FieldItemListInterface $field_item_list */
-    $field_item_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']} : NULL;
-
-    if (!isset($field_item_list)) {
-      // There isn't anything we can do without a valid field.
-      return NULL;
-    }
-
-    $field_item_definition = $field_item_list->getFieldDefinition();
-
-    $values = [];
-    foreach ($field_item_list as $field_item) {
-      /** @var \Drupal\Core\Field\FieldItemInterface $field_item */
-      if ($field) {
-        $values[] = $field_item->$field;
-      }
-      // Find the value using the main property of the field. If no main
-      // property is provided fall back to 'value'.
-      elseif ($main_property_name = $field_item->mainPropertyName()) {
-        $values[] = $field_item->{$main_property_name};
-      }
-      else {
-        $values[] = $field_item->value;
-      }
-    }
-    if ($field_item_definition->getFieldStorageDefinition()->getCardinality() == 1) {
-      return reset($values);
-    }
-    else {
-      return $values;
-    }
-  }
+class Field extends EntityField {
 
 }
diff --git a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
index 4a37b58eb904..87203613ac68 100644
--- a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
+++ b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php
@@ -11,7 +11,7 @@
 /**
  * Tests the "Display all values in the same row" setting.
  *
- * @see \Drupal\views\Plugin\views\field\Field
+ * @see \Drupal\views\Plugin\views\field\EntityField
  *
  * @group views
  */
diff --git a/core/modules/views/src/Tests/Update/EntityViewsDataUpdateTest.php b/core/modules/views/src/Tests/Update/EntityViewsDataUpdateTest.php
index 1778043a6b08..472829cab074 100644
--- a/core/modules/views/src/Tests/Update/EntityViewsDataUpdateTest.php
+++ b/core/modules/views/src/Tests/Update/EntityViewsDataUpdateTest.php
@@ -35,11 +35,11 @@ public function testUpdateHookN() {
     $view->initHandlers();
 
     // Extract the fields from the test view that were updated.
-    /** @var \Drupal\views\Plugin\views\field\Field $field */
+    /** @var \Drupal\views\Plugin\views\field\EntityField $field */
     $created = $view->field['created'];
-    /** @var \Drupal\views\Plugin\views\field\Field $field */
+    /** @var \Drupal\views\Plugin\views\field\EntityField $field */
     $created_1 = $view->field['created_1'];
-    /** @var \Drupal\views\Plugin\views\field\Field $field */
+    /** @var \Drupal\views\Plugin\views\field\EntityField $field */
     $created_2 = $view->field['created_2'];
 
     // Make sure the plugins were converted from date to field.
diff --git a/core/modules/views/src/Tests/ViewResultAssertionTrait.php b/core/modules/views/src/Tests/ViewResultAssertionTrait.php
index 62ae67ce24d8..de5a2aac46a2 100644
--- a/core/modules/views/src/Tests/ViewResultAssertionTrait.php
+++ b/core/modules/views/src/Tests/ViewResultAssertionTrait.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\views\Tests;
 
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 
 /**
  * Provides a class for assertions to check for the expected result of a View.
@@ -91,7 +91,7 @@ protected function assertIdenticalResultsetHelper($view, $expected_result, $colu
         // The comparison will be done on the string representation of the value.
         // For entity fields we don't have the raw value. Let's try to fetch it
         // using the entity itself.
-        elseif (empty($value->$view_column) && isset($view->field[$expected_column]) && ($field = $view->field[$expected_column]) && $field instanceof Field) {
+        elseif (empty($value->$view_column) && isset($view->field[$expected_column]) && ($field = $view->field[$expected_column]) && $field instanceof EntityField) {
           $column = NULL;
           if (count(explode(':', $view_column)) == 2) {
             $column = explode(':', $view_column)[1];
diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
index 1a9df822b065..c82fc6938062 100644
--- a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
+++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php
@@ -8,7 +8,7 @@
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\user\Entity\User;
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
 use Drupal\views\Tests\ViewTestData;
 use Drupal\views\Views;
@@ -16,7 +16,7 @@
 /**
  * Provides some integration tests for the Field handler.
  *
- * @see \Drupal\views\Plugin\views\field\Field
+ * @see \Drupal\views\Plugin\views\field\EntityField
  * @group views
  */
 class FieldFieldTest extends ViewsKernelTestBase {
@@ -229,8 +229,8 @@ public function testSimpleExecute() {
     $executable = Views::getView('test_field_field_test');
     $executable->execute();
 
-    $this->assertTrue($executable->field['id'] instanceof Field);
-    $this->assertTrue($executable->field['field_test'] instanceof Field);
+    $this->assertTrue($executable->field['id'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test'] instanceof EntityField);
 
     $this->assertIdenticalResultset($executable,
       [
@@ -299,9 +299,9 @@ public function testFieldAlias() {
     $executable = Views::getView('test_field_alias_test');
     $executable->execute();
 
-    $this->assertTrue($executable->field['id'] instanceof Field);
-    $this->assertTrue($executable->field['name'] instanceof Field);
-    $this->assertTrue($executable->field['name_alias'] instanceof Field);
+    $this->assertTrue($executable->field['id'] instanceof EntityField);
+    $this->assertTrue($executable->field['name'] instanceof EntityField);
+    $this->assertTrue($executable->field['name_alias'] instanceof EntityField);
 
     $this->assertIdenticalResultset($executable,
       [
@@ -348,10 +348,10 @@ public function testComplexExecute() {
       $timezones[] = $user->getTimeZone();
     }
 
-    $this->assertTrue($executable->field['field_test_multiple'] instanceof Field);
-    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field);
-    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field);
-    $this->assertTrue($executable->field['timezone'] instanceof Field);
+    $this->assertTrue($executable->field['field_test_multiple'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof EntityField);
+    $this->assertTrue($executable->field['timezone'] instanceof EntityField);
 
     $this->assertIdenticalResultset($executable,
       [
@@ -421,8 +421,8 @@ public function testRevisionExecute() {
     $executable = Views::getView('test_field_field_revision_test');
     $executable->execute();
 
-    $this->assertTrue($executable->field['name'] instanceof Field);
-    $this->assertTrue($executable->field['field_test'] instanceof Field);
+    $this->assertTrue($executable->field['name'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test'] instanceof EntityField);
 
     $this->assertIdenticalResultset($executable,
       [
@@ -475,12 +475,12 @@ public function testRevisionComplexExecute() {
       $timezones[] = $user->getTimeZone();
     }
 
-    $this->assertTrue($executable->field['id'] instanceof Field);
-    $this->assertTrue($executable->field['revision_id'] instanceof Field);
-    $this->assertTrue($executable->field['timezone'] instanceof Field);
-    $this->assertTrue($executable->field['field_test_multiple'] instanceof Field);
-    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field);
-    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field);
+    $this->assertTrue($executable->field['id'] instanceof EntityField);
+    $this->assertTrue($executable->field['revision_id'] instanceof EntityField);
+    $this->assertTrue($executable->field['timezone'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test_multiple'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test_multiple_1'] instanceof EntityField);
+    $this->assertTrue($executable->field['field_test_multiple_2'] instanceof EntityField);
 
     $this->assertIdenticalResultset($executable,
       [
@@ -551,7 +551,7 @@ public function testMissingBundleFieldRender() {
   }
 
   /**
-   * Tests \Drupal\views\Plugin\views\field\Field::getValue
+   * Tests \Drupal\views\Plugin\views\field\EntityField::getValue
    */
   public function testGetValueMethod() {
     $bundle = 'test_bundle';
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
index 90bd2ccb2050..2b929f360876 100644
--- a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
@@ -10,12 +10,12 @@
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Tests\views\Unit\Plugin\HandlerTestTrait;
-use Drupal\views\Plugin\views\field\Field;
+use Drupal\views\Plugin\views\field\EntityField;
 use Drupal\views\ResultRow;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 
 /**
- * @coversDefaultClass \Drupal\views\Plugin\views\field\Field
+ * @coversDefaultClass \Drupal\views\Plugin\views\field\EntityField
  * @group views
  */
 class FieldTest extends UnitTestCase {
@@ -107,7 +107,7 @@ public function testConstruct() {
       // provides it.
       'entity field' => 'title',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     $this->assertEquals('title', $handler->definition['field_name']);
   }
@@ -120,7 +120,7 @@ public function testDefineOptionsWithNoOptions() {
       'entity_type' => 'test_entity',
       'field_name' => 'title'
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     // Setup the entity manager to allow fetching the storage definitions.
     $title_storage = $this->getBaseFieldStorage();
@@ -149,7 +149,7 @@ public function testDefineOptionsWithDefaultFormatterOnFieldDefinition() {
       'default_formatter' => 'test_example',
       'default_formatter_settings' => ['link_to_entity' => TRUE]
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     // Setup the entity manager to allow fetching the storage definitions.
     $title_storage = $this->getBaseFieldStorage();
@@ -176,7 +176,7 @@ public function testDefineOptionsWithDefaultFormatterOnFieldType() {
       'field_name' => 'title',
       'default_formatter_settings' => ['link_to_entity' => TRUE]
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     // Setup the entity manager to allow fetching the storage definitions.
     $title_storage = $this->getBaseFieldStorage();
@@ -202,7 +202,7 @@ public function testCalculateDependenciesWithBaseField() {
       'entity_type' => 'test_entity',
       'field_name' => 'title'
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     $title_storage = $this->getBaseFieldStorage();
     $this->entityManager->expects($this->atLeastOnce())
@@ -224,7 +224,7 @@ public function testCalculateDependenciesWithConfiguredField() {
       'entity_type' => 'test_entity',
       'field_name' => 'body'
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
 
     $body_storage = $this->getConfigFieldStorage();
     $this->entityManager->expects($this->atLeastOnce())
@@ -250,7 +250,7 @@ public function testAccess() {
       'entity_type' => 'test_entity',
       'field_name' => 'title',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
     $handler->setViewsData($this->viewsData);
 
@@ -301,7 +301,7 @@ public function testClickSortWithOutConfiguredColumn($order) {
       'entity_type' => 'test_entity',
       'field_name' => 'title',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
 
     $this->entityManager->expects($this->never())
@@ -323,7 +323,7 @@ public function testClickSortWithBaseField($order) {
       'entity_type' => 'test_entity',
       'field_name' => 'title',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
 
     $field_storage = $this->getBaseFieldStorage();
@@ -383,7 +383,7 @@ public function testClickSortWithConfiguredField($order) {
       'entity_type' => 'test_entity',
       'field_name' => 'body',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
 
     $field_storage = $this->getConfigFieldStorage();
@@ -438,7 +438,7 @@ public function testQueryWithGroupByForBaseField() {
       'entity_type' => 'test_entity',
       'field_name' => 'title',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
     $handler->view->field = [$handler];
 
@@ -500,7 +500,7 @@ public function testQueryWithGroupByForConfigField() {
       'entity_type' => 'test_entity',
       'field_name' => 'body',
     ];
-    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new EntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
     $handler->view->field = [$handler];
 
@@ -564,7 +564,7 @@ public function testPrepareItemsByDelta(array $options, array $expected_values)
       'entity_type' => 'test_entity',
       'field_name' => 'integer',
     ];
-    $handler = new FieldTestField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
+    $handler = new FieldTestEntityField([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager, $this->renderer);
     $handler->view = $this->executable;
     $handler->view->field = [$handler];
 
@@ -688,12 +688,12 @@ public function providerSortOrders() {
   /**
    * Setup the mock data needed to make language renderers work.
    *
-   * @param \Drupal\views\Plugin\views\field\Field $handler
+   * @param \Drupal\views\Plugin\views\field\EntityField $handler
    *   The field handler.
    * @param $definition
    *   An array with entity type definition data.
    */
-  protected function setupLanguageRenderer(Field $handler, $definition) {
+  protected function setupLanguageRenderer(EntityField $handler, $definition) {
     $display_handler = $this->getMockBuilder('\Drupal\views\Plugin\views\display\DisplayPluginBase')
       ->disableOriginalConstructor()
       ->getMock();
@@ -724,7 +724,7 @@ protected function setupLanguageRenderer(Field $handler, $definition) {
 
 }
 
-class FieldTestField extends Field {
+class FieldTestEntityField extends EntityField {
 
   public function executePrepareItemsByDelta(array $all_values) {
     return $this->prepareItemsByDelta($all_values);
-- 
GitLab