diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
index a96bdd4..93f65d3 100644
--- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
@@ -92,7 +92,6 @@ public static function createFromFieldStorageDefinition(FieldStorageDefinitionIn
       ->setName($definition->getName())
       ->setProvider($definition->getProvider())
       ->setQueryable($definition->isQueryable())
-      ->setRequired($definition->isRequired())
       ->setRevisionable($definition->isRevisionable())
       ->setSettings($definition->getSettings())
       ->setTargetEntityTypeId($definition->getTargetEntityTypeId())
diff --git a/core/modules/field/config/schema/field.views.schema.yml b/core/modules/field/config/schema/field.views.schema.yml
index 56d852d..33c1ee8 100644
--- a/core/modules/field/config/schema/field.views.schema.yml
+++ b/core/modules/field/config/schema/field.views.schema.yml
@@ -18,7 +18,7 @@ views.argument.field_list_string:
 
 views.field.field:
   type: views_field
-  label: 'Log event message'
+  label: 'Views field field handler'
   mapping:
     click_sort_column:
       type: string
@@ -27,8 +27,9 @@ views.field.field:
       type: string
       label: 'Formatter'
     settings:
-      type: sequence
       label: 'Settings'
+      type: field.formatter.settings.[%parent.type]
+      type: sequence
       sequence:
         - type: string
           label: 'Setting'
diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php
index 106ce87..06b741e 100644
--- a/core/modules/field/src/Plugin/views/field/Field.php
+++ b/core/modules/field/src/Plugin/views/field/Field.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
 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;
@@ -21,6 +22,7 @@
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\field\FieldStorageConfigInterface;
 use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\Plugin\views\field\FieldPluginBase;
@@ -113,6 +115,13 @@ class Field extends FieldPluginBase implements CacheablePluginInterface {
   protected $languageManager;
 
   /**
+   * The field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypePluginManager;
+
+  /**
    * Constructs a \Drupal\field\Plugin\views\field\Field object.
    *
    * @param array $configuration
@@ -128,12 +137,19 @@ class Field extends FieldPluginBase implements CacheablePluginInterface {
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, LanguageManagerInterface $language_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityManagerInterface $entity_manager, FormatterPluginManager $formatter_plugin_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, LanguageManagerInterface $language_manager) {
     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;
+
+    // @todo On the longrun just use either 'entity field' or 'field_name' all
+    //   over the project.
+    if (isset($this->definition['entity field'])) {
+      $this->definition['field_name'] = $this->definition['entity field'];
+    }
   }
 
   /**
@@ -146,6 +162,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('entity.manager'),
       $container->get('plugin.manager.field.formatter'),
+      $container->get('plugin.manager.field.field_type'),
       $container->get('language_manager')
     );
   }
@@ -158,26 +175,13 @@ public static function create(ContainerInterface $container, array $configuratio
    */
   protected function getFieldDefinition() {
     if (!$this->fieldDefinition) {
-      $field_storage_config = $this->getFieldStorageConfig();
+      $field_storage_config = $this->getFieldStorageDefinition();
       $this->fieldDefinition = BaseFieldDefinition::createFromFieldStorageDefinition($field_storage_config);
     }
     return $this->fieldDefinition;
   }
 
   /**
-   * Gets the field configuration.
-   *
-   * @return \Drupal\field\FieldStorageConfigInterface
-   */
-  protected function getFieldStorageConfig() {
-    if (!$this->fieldStorageConfig) {
-      $field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($this->definition['entity_type']);
-      $this->fieldStorageConfig = $field_storage_definitions[$this->definition['field_name']];
-    }
-    return $this->fieldStorageConfig;
-  }
-
-  /**
    * Overrides \Drupal\views\Plugin\views\field\FieldPluginBase::init().
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
@@ -214,48 +218,17 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
    * {@inheritdoc}
    */
   public function access(AccountInterface $account) {
-    $base_table = $this->get_base_table();
-    $access_control_handler = $this->entityManager->getAccessControlHandler($this->definition['entity_tables'][$base_table]);
+    $access_control_handler = $this->entityManager->getAccessControlHandler($this->getEntityType());
     return $access_control_handler->fieldAccess('view', $this->getFieldDefinition(), $account);
   }
 
   /**
-   * Set the base_table and base_table_alias.
-   *
-   * @return string
-   *   The base table which is used in the current view "context".
-   */
-  function get_base_table() {
-    if (!isset($this->base_table)) {
-      // This base_table is coming from the entity not the field.
-      $this->base_table = $this->view->storage->get('base_table');
-
-      // If the current field is under a relationship you can't be sure that the
-      // base table of the view is the base table of the current field.
-      // For example a field from a node author on a node view does have users as base table.
-      if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
-        $relationships = $this->view->display_handler->getOption('relationships');
-        if (!empty($relationships[$this->options['relationship']])) {
-          $options = $relationships[$this->options['relationship']];
-          $data = Views::viewsData()->get($options['table']);
-          $this->base_table = $data[$options['field']]['relationship']['base'];
-        }
-      }
-    }
-
-    return $this->base_table;
-  }
-
-  /**
    * 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) {
-    $this->get_base_table();
-
-    $entity_type = $this->definition['entity_tables'][$this->base_table];
     $fields = $this->additional_fields;
     // No need to add the entity type.
     $entity_type_key = array_search('entity_type', $fields);
@@ -273,15 +246,11 @@ public function query($use_groupby = FALSE) {
       $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : array();
 
       $fields = array();
-      $rkey = $this->definition['is revision'] ? EntityStorageInterface::FIELD_LOAD_REVISION : EntityStorageInterface::FIELD_LOAD_CURRENT;
+      // @todo what we are doing here is right?
+      $rkey = !empty($this->definition['is revision']) ? EntityStorageInterface::FIELD_LOAD_REVISION : EntityStorageInterface::FIELD_LOAD_CURRENT;
       // Go through the list and determine the actual column name from field api.
       foreach ($options as $column) {
-        $name = $column;
-        if (isset($field_definition['storage_details']['sql'][$rkey][$this->table][$column])) {
-          $name = $field_definition['storage_details']['sql'][$rkey][$this->table][$column];
-        }
-
-        $fields[$column] = $name;
+        $fields[$column] = $this->getTableMapping()->getFieldColumnName($this->getFieldStorageDefinition(), $column);
       }
 
       $this->group_fields = $fields;
@@ -353,12 +322,8 @@ public function clickSort($order) {
     }
 
     $this->ensureMyTable();
-    $entity_type_id = $this->definition['entity_type'];
-    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
-    $field_storage = $field_storage_definitions[$this->definition['field_name']];
-    /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
-    $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
-    $column = $table_mapping->getFieldColumnName($field_storage, $this->options['click_sort_column']);
+    $field_storage = $this->getFieldStorageDefinition();
+    $column = $this->getTableMapping()->getFieldColumnName($field_storage, $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;
@@ -366,12 +331,34 @@ public function clickSort($order) {
     $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 the two options here.
+    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_definitions = $this->entityManager->getFieldStorageDefinitions($this->definition['entity_type']);
-    $field_storage = $field_storage_definitions[$this->definition['field_name']];
-    $field_type = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_storage->getType());
+    $field_storage = $this->getFieldStorageDefinition();
+    $field_type = $this->fieldTypePluginManager->getDefinition($field_storage->getType());
     $column_names = array_keys($field_storage->getColumns());
     $default_column = '';
     // Try to determine a sensible default.
@@ -386,11 +373,13 @@ protected function defineOptions() {
     $options['click_sort_column'] = array(
       'default' => $default_column,
     );
+
     $options['type'] = array(
-      'default' => $field_type['default_formatter'],
+      'default' => isset($field_type['default_formatter']) ? $field_type['default_formatter'] : '',
     );
+
     $options['settings'] = array(
-      'default' => array(),
+      'default' => isset($this->definition['default_formatter_settings']) ? $this->definition['default_formatter_settings'] : [],
     );
     $options['group_column'] = array(
       'default' => $default_column,
@@ -943,7 +932,12 @@ function field_langcode(EntityInterface $entity) {
    */
   public function calculateDependencies() {
     // Add the module providing the configured field storage as a dependency.
-    return array('config' => array($this->getFieldStorageConfig()->getConfigDependencyName()));
+
+    if (($field_storage = $this->getFieldStorageDefinition()) && $field_storage instanceof FieldStorageConfigInterface) {
+      return ['config' => [$field_storage->getConfigDependencyName()]];
+    }
+
+    return [];
   }
 
   /**
@@ -965,4 +959,10 @@ public function getCacheContexts() {
     return $contexts;
   }
 
+  /**
+   * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
+   */
+  protected function getTableMapping() {
+    return $this->entityManager->getStorage($this->definition['entity_type'])->getTableMapping();
+  }
 }
diff --git a/core/modules/field/tests/src/Unit/Plugin/views/field/FieldTest.php b/core/modules/field/tests/src/Unit/Plugin/views/field/FieldTest.php
new file mode 100644
index 0000000..c982fe6
--- /dev/null
+++ b/core/modules/field/tests/src/Unit/Plugin/views/field/FieldTest.php
@@ -0,0 +1,535 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\field\Unit\Plugin\views\field\FieldTest.
+ */
+
+namespace Drupal\Tests\field\Unit\Plugin\views\field;
+
+use Drupal\field\Plugin\views\field\Field;
+use Drupal\Tests\UnitTestCase;
+use Drupal\Tests\views\Unit\Plugin\HandlerTestTrait;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+
+/**
+ * @coversDefaultClass \Drupal\views\Plugin\views\field\Field
+ * @group views
+ */
+class FieldTest extends UnitTestCase {
+
+  use HandlerTestTrait;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  protected $formatterPluginManager;
+
+  protected $languageManager;
+
+  /**
+   * The mocked field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $fieldTypePluginManager;
+
+  /**
+   * The mocked display plugin.
+   *
+   * @var \Drupal\views\Plugin\views\display\DisplayPluginBase|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $display;
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    $this->formatterPluginManager = $this->getMockBuilder('Drupal\Core\Field\FormatterPluginManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $this->fieldTypePluginManager = $this->getMock('Drupal\Core\Field\FieldTypePluginManagerInterface');
+    $this->fieldTypePluginManager->expects($this->any())
+      ->method('getDefaultStorageSettings')
+      ->willReturn([]);
+    $this->fieldTypePluginManager->expects($this->any())
+      ->method('getDefaultFieldSettings')
+      ->willReturn([]);
+
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+
+    $this->setupExecutableAndView();
+    $this->setupViewsData();
+    $this->display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $container = new ContainerBuilder();
+    $container->set('plugin.manager.field.field_type', $this->fieldTypePluginManager);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * @covers ::__construct
+   */
+  public function testConstruct() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      // Just provide 'entity field' as definition. This is how EntityViewsData
+      // provides it.
+      'entity field' => 'title',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+
+    $this->assertEquals('title', $handler->definition['field_name']);
+  }
+
+  /**
+   * @covers ::defineOptions()
+   */
+  public function testDefineOptionsWithNoOptions() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title'
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+
+    // Setup the entity manager to allow fetching the storage definitions.
+    $title_storage = $this->getBaseFieldStorage();
+
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $title_storage,
+      ]);
+
+    $options = [];
+    $handler->init($this->executable, $this->display, $options);
+
+    $this->assertEquals('value', $handler->options['group_column']);
+    $this->assertEquals('all', $handler->options['delta_limit']);
+  }
+
+  /**
+   * @covers ::defineOptions()
+   */
+  public function testDefineOptionsWithDefaultFormatter() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title',
+      'default_formatter_settings' => ['link_to_entity' => TRUE]
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+
+    // Setup the entity manager to allow fetching the storage definitions.
+    $title_storage = $this->getBaseFieldStorage();
+
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $title_storage,
+      ]);
+
+    $options = [];
+    $handler->init($this->executable, $this->display, $options);
+
+    $this->assertEquals(['link_to_entity' => TRUE], $handler->options['settings']);
+  }
+
+  /**
+   * @covers ::calculateDependencies()
+   */
+  public function testCalculateDependenciesWithBaseField() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title'
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+
+    $title_storage = $this->getBaseFieldStorage();
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $title_storage,
+      ]);
+
+    $dependencies = $handler->calculateDependencies();
+    $this->assertEmpty($dependencies);
+  }
+
+  /**
+   * @covers ::calculateDependencies()
+   */
+  public function testCalculateDependenciesWithConfiguredField() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'body'
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+
+    $body_storage = $this->getConfigFieldStorage();
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'body' => $body_storage,
+      ]);
+
+    $body_storage->expects($this->atLeastOnce())
+      ->method('getConfigDependencyName')
+      ->willReturn('field.field_storage_config.body');
+
+    $dependencies = $handler->calculateDependencies();
+    $this->assertEquals(['config' => ['field.field_storage_config.body']], $dependencies);
+  }
+
+  /**
+   * @covers ::access
+   */
+  public function testAccess() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+    $handler->setViewsData($this->viewsData);
+
+    $this->view->expects($this->atLeastOnce())
+      ->method('get')
+      ->with('base_table')
+      ->willReturn('test_entity_table');
+
+    $this->viewsData->expects($this->atLeastOnce())
+      ->method('get')
+      ->with('test_entity_table')
+      ->willReturn([
+        'table' => ['entity type' => 'test_entity']
+      ]);
+
+    $access_control_handler = $this->getMock('Drupal\Core\Entity\EntityAccessControlHandlerInterface');
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getAccessControlHandler')
+      ->with('test_entity')
+      ->willReturn($access_control_handler);
+
+    $title_storage = $this->getBaseFieldStorage();
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $title_storage,
+      ]);
+
+    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
+
+    $access_control_handler->expects($this->atLeastOnce())
+      ->method('fieldAccess')
+      // @todo replace the second anything() with FALSE.
+      ->with('view', $this->anything(), $account, NULL, $this->anything())
+      ->willReturn(TRUE);
+
+    $this->assertTrue($handler->access($account));
+  }
+
+  /**
+   * @dataProvider providerSortOrders
+   *
+   * @param string $order
+   *   The sort order.
+   */
+  public function testClickSortWithOutConfiguredColumn($order) {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+
+    $this->entityManager->expects($this->never())
+      ->method('getFieldStorageDefinitions');
+
+    $handler->clickSort($order);
+  }
+
+  /**
+   * @dataProvider providerSortOrders
+   *
+   * @param string $order
+   *   The sort order.
+   */
+  public function testClickSortWithBaseField($order) {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+
+    $field_storage = $this->getBaseFieldStorage();
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $field_storage,
+      ]);
+
+    $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+    $table_mapping
+      ->expects($this->atLeastOnce())
+      ->method('getFieldColumnName')
+      ->with($field_storage, 'value')
+      ->willReturn('title');
+    $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface');
+    $entity_storage->expects($this->atLeastOnce())
+      ->method('getTableMapping')
+      ->willReturn($table_mapping);
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getStorage')
+      ->with('test_entity')
+      ->willReturn($entity_storage);
+
+    // Setup a click sort configuration.
+    $options = [
+      'click_sort_column' => 'value',
+      'table' => 'test_entity',
+    ];
+    $handler->init($this->executable, $this->display, $options);
+
+    $handler->query = $this->getMockBuilder('Drupal\views\Plugin\views\query\Sql')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $handler->query->expects($this->atLeastOnce())
+      ->method('ensureTable')
+      ->with('test_entity', NULL)
+      ->willReturn('test_entity');
+
+    $handler->query->expects($this->atLeastOnce())
+      ->method('addOrderBy')
+      ->with(NULL, NULL, $order, 'test_entity.title', []);
+    $handler->clickSort($order);
+  }
+
+  /**
+   * @dataProvider providerSortOrders
+   *
+   * @param string $order
+   *   The sort order.
+   */
+  public function testClickSortWithConfiguredField($order) {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'body',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+
+    $field_storage = $this->getConfigFieldStorage();
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'body' => $field_storage,
+      ]);
+
+    $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+    $table_mapping
+      ->expects($this->atLeastOnce())
+      ->method('getFieldColumnName')
+      ->with($field_storage, 'value')
+      ->willReturn('body_value');
+    $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface');
+    $entity_storage->expects($this->atLeastOnce())
+      ->method('getTableMapping')
+      ->willReturn($table_mapping);
+    $this->entityManager->expects($this->atLeastOnce())
+      ->method('getStorage')
+      ->with('test_entity')
+      ->willReturn($entity_storage);
+
+    // Setup a click sort configuration.
+    $options = [
+      'click_sort_column' => 'value',
+      'table' => 'test_entity__body',
+    ];
+    $handler->init($this->executable, $this->display, $options);
+
+    $handler->query = $this->getMockBuilder('Drupal\views\Plugin\views\query\Sql')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $handler->query->expects($this->atLeastOnce())
+      ->method('ensureTable')
+      ->with('test_entity__body', NULL)
+      ->willReturn('test_entity__body_alias');
+
+    $handler->query->expects($this->atLeastOnce())
+      ->method('addOrderBy')
+      ->with(NULL, NULL, $order, 'test_entity__body_alias.body_value', []);
+    $handler->clickSort($order);
+  }
+
+  public function testQueryWithGroupByForBaseField() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'title',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+
+    $field_storage = $this->getBaseFieldStorage();
+    $this->entityManager->expects($this->any())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'title' => $field_storage,
+      ]);
+
+    $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+    $table_mapping
+      ->expects($this->any())
+      ->method('getFieldColumnName')
+      ->with($field_storage, 'value')
+      ->willReturn('title');
+    $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface');
+    $entity_storage->expects($this->any())
+      ->method('getTableMapping')
+      ->willReturn($table_mapping);
+    $this->entityManager->expects($this->any())
+      ->method('getStorage')
+      ->with('test_entity')
+      ->willReturn($entity_storage);
+
+    $options = [
+      'group_column' => 'value',
+      'group_columns' => [],
+      'table' => 'test_entity_table',
+    ];
+    $handler->init($this->executable, $this->display, $options);
+
+    $query = $this->getMockBuilder('Drupal\views\Plugin\views\query\Sql')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $query->expects($this->once())
+      ->method('ensureTable')
+      ->with('test_entity_table', NULL)
+      ->willReturn('test_entity_table');
+    // Ensure that we add the title field to the query, if we group by some
+    // other field in the view.
+    $query->expects($this->once())
+      ->method('addField')
+      ->with('test_entity_table', 'title');
+
+    $this->executable->query = $query;
+
+    $handler->query(TRUE);
+  }
+
+  public function testQueryWithGroupByForConfigField() {
+    $definition = [
+      'entity_type' => 'test_entity',
+      'field_name' => 'body',
+    ];
+    $handler = new Field([], 'field', $definition, $this->entityManager, $this->formatterPluginManager, $this->fieldTypePluginManager, $this->languageManager);
+    $handler->view = $this->executable;
+
+    $field_storage = $this->getConfigFieldStorage();
+    $this->entityManager->expects($this->any())
+      ->method('getFieldStorageDefinitions')
+      ->with('test_entity')
+      ->willReturn([
+        'body' => $field_storage,
+      ]);
+
+    $table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
+    $table_mapping
+      ->expects($this->any())
+      ->method('getFieldColumnName')
+      ->with($field_storage, 'value')
+      ->willReturn('body_value');
+    $entity_storage = $this->getMock('Drupal\Core\Entity\Sql\SqlEntityStorageInterface');
+    $entity_storage->expects($this->any())
+      ->method('getTableMapping')
+      ->willReturn($table_mapping);
+    $this->entityManager->expects($this->any())
+      ->method('getStorage')
+      ->with('test_entity')
+      ->willReturn($entity_storage);
+
+    $options = [
+      'group_column' => 'value',
+      'group_columns' => [],
+      'table' => 'test_entity__body',
+    ];
+    $handler->init($this->executable, $this->display, $options);
+
+    $query = $this->getMockBuilder('Drupal\views\Plugin\views\query\Sql')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $query->expects($this->once())
+      ->method('ensureTable')
+      ->with('test_entity__body', NULL)
+      ->willReturn('test_entity__body');
+    // Ensure that we add the title field to the query, if we group by some
+    // other field in the view.
+    $query->expects($this->once())
+      ->method('addField')
+      ->with('test_entity__body', 'body_value');
+
+    $this->executable->query = $query;
+
+    $handler->query(TRUE);
+  }
+
+  /**
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected function getBaseFieldStorage() {
+    $title_storage = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
+    $title_storage->expects($this->any())
+      ->method('getColumns')
+      ->willReturn(['value' => ['type' => 'varchar']]);
+    $title_storage->expects($this->any())
+      ->method('getSettings')
+      ->willReturn([]);
+    $title_storage->expects($this->any())
+      ->method('getConstraints')
+      ->willReturn([]);
+    return $title_storage;
+  }
+
+  /**
+   * @return \Drupal\field\FieldStorageConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected function getConfigFieldStorage() {
+    $title_storage = $this->getMock('Drupal\field\FieldStorageConfigInterface');
+    $title_storage->expects($this->any())
+      ->method('getColumns')
+      ->willReturn(['value' => ['type' => 'varchar']]);
+    $title_storage->expects($this->any())
+      ->method('getSettings')
+      ->willReturn([]);
+    $title_storage->expects($this->any())
+      ->method('getConstraints')
+      ->willReturn([]);
+    return $title_storage;
+  }
+
+  public function providerSortOrders() {
+    return [
+      ['asc'],
+      ['desc'],
+      ['ASC'],
+      ['DESC'],
+    ];
+  }
+
+}
+
diff --git a/core/modules/node/config/install/views.view.content.yml b/core/modules/node/config/install/views.view.content.yml
index 2a44101..b9fe7de 100644
--- a/core/modules/node/config/install/views.view.content.yml
+++ b/core/modules/node/config/install/views.view.content.yml
@@ -163,10 +163,12 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_node: true
-          plugin_id: node
           entity_type: node
           entity_field: title
+          type: string
+          settings:
+            link_to_entity: true
+          plugin_id: field
         type:
           id: type
           table: node_field_data
@@ -246,7 +248,7 @@ display:
           date_format: short
           custom_date_format: ''
           timezone: ''
-          plugin_id: date
+          plugin_id: field
           entity_type: node
           entity_field: changed
         edit_node:
diff --git a/core/modules/node/config/install/views.view.content_recent.yml b/core/modules/node/config/install/views.view.content_recent.yml
index 9456436..cec60cd 100644
--- a/core/modules/node/config/install/views.view.content_recent.yml
+++ b/core/modules/node/config/install/views.view.content_recent.yml
@@ -155,8 +155,10 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_node: true
-          plugin_id: node
+          type: string
+          settings:
+            link_to_entity: true
+          plugin_id: field
           entity_type: node
           entity_field: title
         name:
diff --git a/core/modules/node/src/NodeViewsData.php b/core/modules/node/src/NodeViewsData.php
index b3df204..01b68dc 100644
--- a/core/modules/node/src/NodeViewsData.php
+++ b/core/modules/node/src/NodeViewsData.php
@@ -33,7 +33,12 @@ public function getViewsData() {
       'validate type' => 'nid',
     ];
 
-    $data['node_field_data']['title']['field']['id'] = 'node';
+    // @fixme Move this into the generic views data.
+    $data['node_field_data']['title']['field']['id'] = 'field';
+    $data['node_field_data']['title']['field']['field_name'] = 'title';
+    $data['node_field_data']['title']['field']['entity_tables'] = ['node' => 'node', 'node_field_data' => 'node'];
+    $data['node_field_data']['title']['field']['default_formatter_settings'] = ['entity_link' => TRUE];
+
     $data['node_field_data']['title']['field']['link_to_node default'] = TRUE;
 
     $data['node_field_data']['type']['field']['id'] = 'node_type';
diff --git a/core/modules/node/src/Plugin/views/wizard/Node.php b/core/modules/node/src/Plugin/views/wizard/Node.php
index b9c66e0..400c6d1 100644
--- a/core/modules/node/src/Plugin/views/wizard/Node.php
+++ b/core/modules/node/src/Plugin/views/wizard/Node.php
@@ -88,6 +88,7 @@ protected function defaultDisplayOptions() {
     $display_options['fields']['title']['table'] = 'node_field_data';
     $display_options['fields']['title']['field'] = 'title';
     $display_options['fields']['title']['entity_type'] = 'node';
+    $display_options['fields']['title']['entity_type'] = 'node';
     $display_options['fields']['title']['entity_field'] = 'title';
     $display_options['fields']['title']['label'] = '';
     $display_options['fields']['title']['alter']['alter_text'] = 0;
@@ -100,8 +101,8 @@ protected function defaultDisplayOptions() {
     $display_options['fields']['title']['alter']['html'] = 0;
     $display_options['fields']['title']['hide_empty'] = 0;
     $display_options['fields']['title']['empty_zero'] = 0;
-    $display_options['fields']['title']['link_to_node'] = 1;
-    $display_options['fields']['title']['plugin_id'] = 'node';
+    $display_options['fields']['title']['settings']['link_to_entity'] = 1;
+    $display_options['fields']['title']['plugin_id'] = 'field';
 
     return $display_options;
   }
@@ -176,8 +177,8 @@ protected  function display_options_row(&$display_options, $row_plugin, $row_opt
         $display_options['fields']['title']['id'] = 'title';
         $display_options['fields']['title']['table'] = 'node_field_data';
         $display_options['fields']['title']['field'] = 'title';
-        $display_options['fields']['title']['link_to_node'] = ($row_plugin == 'titles_linked');
-        $display_options['fields']['title']['plugin_id'] = 'node';
+        $display_options['fields']['title']['settings']['link_to_entity'] = $row_plugin === 'titles_linked';
+        $display_options['fields']['title']['plugin_id'] = 'field';
         break;
     }
   }
diff --git a/core/modules/tracker/src/Tests/Views/TrackerUserUidTest.php b/core/modules/tracker/src/Tests/Views/TrackerUserUidTest.php
index 7c9be32..ce5bbab 100644
--- a/core/modules/tracker/src/Tests/Views/TrackerUserUidTest.php
+++ b/core/modules/tracker/src/Tests/Views/TrackerUserUidTest.php
@@ -29,13 +29,11 @@ class TrackerUserUidTest extends TrackerTestBase {
   public function testUserUid() {
     $map = array(
       'nid' => 'nid',
-      'node_field_data_title' => 'title',
     );
 
     $expected = array(
       array(
         'nid' => $this->node->id(),
-        'title' => $this->node->label(),
       )
     );
 
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index 9f361c7..d61caec 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -293,28 +293,28 @@ protected function mapSingleFieldViewsData($table, $field_name, $field_type, $co
       case 'timestamp':
       case 'created':
       case 'changed':
-        $views_field['field']['id'] = 'date';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'date';
         $views_field['filter']['id'] = 'date';
         $views_field['sort']['id'] = 'date';
         break;
 
       case 'language':
-        $views_field['field']['id'] = 'language';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'language';
         $views_field['filter']['id'] = 'language';
         $views_field['sort']['id'] = 'standard';
         break;
 
       case 'boolean':
-        $views_field['field']['id'] = 'boolean';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'numeric';
         $views_field['filter']['id'] = 'boolean';
         $views_field['sort']['id'] = 'standard';
         break;
 
       case 'uri':
-        $views_field['field']['id'] = 'url';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'string';
         $views_field['filter']['id'] = 'string';
         $views_field['sort']['id'] = 'standard';
@@ -339,7 +339,7 @@ protected function mapSingleFieldViewsData($table, $field_name, $field_type, $co
           case 'float':
           case 'double':
           case 'decimal':
-            $views_field['field']['id'] = 'numeric';
+            $views_field['field']['id'] = 'field';
             $views_field['argument']['id'] = 'numeric';
             $views_field['filter']['id'] = 'numeric';
             $views_field['sort']['id'] = 'standard';
@@ -352,14 +352,14 @@ protected function mapSingleFieldViewsData($table, $field_name, $field_type, $co
           case 'text':
           case 'mediumtext':
           case 'longtext':
-            $views_field['field']['id'] = 'standard';
+            $views_field['field']['id'] = 'field';
             $views_field['argument']['id'] = 'string';
             $views_field['filter']['id'] = 'string';
             $views_field['sort']['id'] = 'standard';
             break;
 
           default:
-            $views_field['field']['id'] = 'standard';
+            $views_field['field']['id'] = 'field';
             $views_field['argument']['id'] = 'standard';
             $views_field['filter']['id'] = 'standard';
             $views_field['sort']['id'] = 'standard';
@@ -430,13 +430,13 @@ protected function processViewsDataForEntityReference($table, FieldDefinitionInt
           'title' => $entity_type->getLabel(),
           'id' => 'standard',
         ];
-        $views_field['field']['id'] = 'numeric';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'numeric';
         $views_field['filter']['id'] = 'numeric';
         $views_field['sort']['id'] = 'standard';
       }
       else {
-        $views_field['field']['id'] = 'standard';
+        $views_field['field']['id'] = 'field';
         $views_field['argument']['id'] = 'string';
         $views_field['filter']['id'] = 'string';
         $views_field['sort']['id'] = 'standard';
@@ -466,7 +466,7 @@ protected function processViewsDataForTextLong($table, FieldDefinitionInterface
     // Connect the text field to its formatter.
     if ($field_column_name == 'value') {
       $views_field['field']['format'] = $field_definition->getName() . '__format';
-      $views_field['field']['id'] = 'markup';
+      $views_field['field']['id'] = 'field';
     }
   }
 
diff --git a/core/modules/views/src/Plugin/ViewsHandlerManager.php b/core/modules/views/src/Plugin/ViewsHandlerManager.php
index ac7abda..a99760d 100644
--- a/core/modules/views/src/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/src/Plugin/ViewsHandlerManager.php
@@ -91,7 +91,7 @@ public function getHandler($item, $override = NULL) {
 
     if (isset($data[$field][$this->handlerType])) {
       $definition = $data[$field][$this->handlerType];
-      foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table', 'entity field') as $key) {
+      foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table', 'entity type', 'entity field') as $key) {
         if (!isset($definition[$key])) {
           // First check the field level.
           if (!empty($data[$field][$key])) {
@@ -99,7 +99,8 @@ public function getHandler($item, $override = NULL) {
           }
           // Then if that doesn't work, check the table level.
           elseif (!empty($data['table'][$key])) {
-            $definition[$key] = $data['table'][$key];
+            $definition_key = $key === 'entity type' ? 'entity_type' : $key;
+            $definition[$definition_key] = $data['table'][$key];
           }
         }
       }
diff --git a/core/modules/views/src/Tests/ModuleTest.php b/core/modules/views/src/Tests/ModuleTest.php
index 1f52a19..e801a09 100644
--- a/core/modules/views/src/Tests/ModuleTest.php
+++ b/core/modules/views/src/Tests/ModuleTest.php
@@ -30,7 +30,7 @@ class ModuleTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('user', 'block');
+  public static $modules = ['field', 'user', 'block'];
 
   /**
    * Stores the last triggered error, for example via debug().
@@ -142,8 +142,8 @@ public function customErrorHandler($error_level, $message, $filename, $line, $co
    * Tests the load wrapper/helper functions.
    */
   public function testLoadFunctions() {
-    $this->enableModules(array('node'));
-    $this->installConfig(array('node'));
+    $this->enableModules(['text', 'node']);
+    $this->installConfig(['node']);
     $storage = $this->container->get('entity.manager')->getStorage('view');
 
     // Test views_view_is_enabled/disabled.
diff --git a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php b/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php
index b7ece54..2861d69 100644
--- a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php
+++ b/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php
@@ -28,7 +28,7 @@ class BlockDependenciesTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('node', 'block', 'user');
+  public static $modules = array('node', 'block', 'user', 'field');
 
   /**
    * Tests that exposed filter blocks have the correct dependencies.
diff --git a/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php b/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php
index edb2e79..f3f1c11 100644
--- a/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php
+++ b/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\views\Tests\Wizard;
 
+use Drupal\views\Entity\View;
+
 /**
  * Tests the ability of the views wizard to specify the number of items per
  * page.
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_dropbutton.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_dropbutton.yml
index f55087f..3f4e50f 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_dropbutton.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_dropbutton.yml
@@ -92,7 +92,7 @@ display:
           id: title
           table: node_field_data
           field: title
-          plugin_id: node
+          plugin_id: field
           label: ''
           alter:
             alter_text: false
@@ -105,7 +105,9 @@ display:
             html: false
           hide_empty: false
           empty_zero: false
-          link_to_node: true
+          type: string
+          settings:
+            link_to_entity: false
           entity_type: node
           entity_field: title
         nothing:
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml
index a7729b1..02ac10a 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_search.yml
@@ -73,7 +73,6 @@ display:
             html: false
           hide_empty: false
           empty_zero: false
-          link_to_node: true
           relationship: none
           group_type: group
           admin_label: ''
@@ -89,9 +88,12 @@ display:
           element_default_classes: true
           empty: ''
           hide_alter_empty: true
-          plugin_id: node
           entity_type: node
           entity_field: title
+          plugin_id: field
+          type: string
+          settings:
+            link_to_entity: true
       filters:
         status:
           value: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml
index 4399c42..f030421 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml
@@ -53,10 +53,12 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_node: true
-          plugin_id: node
           entity_type: node
           entity_field: title
+          type: string
+          settings:
+            link_to_entity: true
+          plugin_id: field
       filters:
         type:
           id: type
diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
index e9c5a60..34ba26d 100644
--- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
+++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
@@ -633,7 +633,7 @@ protected function assertField($data, $field_name) {
    *   The views data to check.
    */
   protected function assertStringField($data) {
-    $this->assertEquals('standard', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('string', $data['filter']['id']);
     $this->assertEquals('string', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -646,7 +646,7 @@ protected function assertStringField($data) {
    *   The views data to check.
    */
   protected function assertUriField($data) {
-    $this->assertEquals('url', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('string', $data['filter']['id']);
     $this->assertEquals('string', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -662,7 +662,7 @@ protected function assertUriField($data) {
    */
   protected function assertLongTextField($data, $field_name) {
     $value_field = $data[$field_name . '__value'];
-    $this->assertEquals('markup', $value_field['field']['id']);
+    $this->assertEquals('field', $value_field['field']['id']);
     $this->assertEquals($field_name . '__format', $value_field['field']['format']);
     $this->assertEquals('string', $value_field['filter']['id']);
     $this->assertEquals('string', $value_field['argument']['id']);
@@ -679,7 +679,7 @@ protected function assertLongTextField($data, $field_name) {
    */
   protected function assertUuidField($data) {
     // @todo Can we provide additional support for UUIDs in views?
-    $this->assertEquals('standard', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('string', $data['filter']['id']);
     $this->assertEquals('string', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -692,7 +692,7 @@ protected function assertUuidField($data) {
    *   The views data to check.
    */
   protected function assertNumericField($data) {
-    $this->assertEquals('numeric', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('numeric', $data['filter']['id']);
     $this->assertEquals('numeric', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -705,7 +705,7 @@ protected function assertNumericField($data) {
    *   The views data to check.
    */
   protected function assertLanguageField($data) {
-    $this->assertEquals('language', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('language', $data['filter']['id']);
     $this->assertEquals('language', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -715,7 +715,7 @@ protected function assertLanguageField($data) {
    * Tests views data for a entity reference field.
    */
   protected function assertEntityReferenceField($data) {
-    $this->assertEquals('numeric', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('numeric', $data['filter']['id']);
     $this->assertEquals('numeric', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
@@ -725,7 +725,7 @@ protected function assertEntityReferenceField($data) {
    * Tests views data for a bundle field.
    */
   protected function assertBundleField($data) {
-    $this->assertEquals('standard', $data['field']['id']);
+    $this->assertEquals('field', $data['field']['id']);
     $this->assertEquals('bundle', $data['filter']['id']);
     $this->assertEquals('string', $data['argument']['id']);
     $this->assertEquals('standard', $data['sort']['id']);
diff --git a/core/modules/views/tests/src/Unit/Plugin/HandlerBaseTest.php b/core/modules/views/tests/src/Unit/Plugin/HandlerBaseTest.php
new file mode 100644
index 0000000..4118a42
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/HandlerBaseTest.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\views\Unit\Plugin\HandlerBaseTest.
+ */
+
+namespace Drupal\Tests\views\Unit\Plugin;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\views\Plugin\views\HandlerBase;
+
+/**
+ * @coversDefaultClass \Drupal\views\Plugin\views\HandlerBase
+ * @group Views
+ */
+class HandlerBaseTest extends UnitTestCase {
+
+  use HandlerTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->setupViewsData();
+    $this->setupExecutableAndView();
+  }
+
+  /**
+   * @covers ::getEntityType
+   */
+  public function testGetEntityTypeForFieldOnBaseTable() {
+    $handler = new TestHandler([], 'test_handler', []);
+    $handler->view = $this->executable;
+
+    $this->view->expects($this->any())
+      ->method('get')
+      ->with('base_table')
+      ->willReturn('test_entity_type_table');
+    $this->viewsData->expects($this->any())
+      ->method('get')
+      ->with('test_entity_type_table')
+      ->willReturn([
+        'table' => ['entity type' => 'test_entity_type']
+      ]);
+    $handler->setViewsData($this->viewsData);
+
+    $this->assertEquals('test_entity_type', $handler->getEntityType());
+  }
+
+  /**
+   * @covers ::getEntityType
+   */
+  public function testGetEntityTypeForFieldWithRelationship() {
+    $handler = new TestHandler([], 'test_handler', []);
+    $handler->view = $this->executable;
+
+    $this->view->expects($this->any())
+      ->method('get')
+      ->with('base_table')
+      ->willReturn('test_entity_type_table');
+    $this->viewsData->expects($this->any())
+      ->method('get')
+      ->with('test_other_entity_type_table')
+      ->willReturn([
+        'table' => ['entity type' => 'test_other_entity_type']
+      ]);
+    $handler->setViewsData($this->viewsData);
+
+    $this->markTestIncomplete('Needs to be implemented and getEntityType() has to be fixed.');
+
+    $this->assertEquals('test_other_entity_type', $handler->getEntityType());
+  }
+
+}
+
+class TestHandler extends HandlerBase {
+
+}
diff --git a/core/modules/views/tests/src/Unit/Plugin/HandlerTestTrait.php b/core/modules/views/tests/src/Unit/Plugin/HandlerTestTrait.php
new file mode 100644
index 0000000..703f6f6
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/HandlerTestTrait.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\views\Unit\Plugin\HandlerTestTrait.
+ */
+
+namespace Drupal\Tests\views\Unit\Plugin;
+
+trait HandlerTestTrait {
+
+  /**
+   * The mocked view entity.
+   *
+   * @var \Drupal\views\Entity\View|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $view;
+
+  /**
+   * The mocked view executable.
+   *
+   * @var \Drupal\views\ViewExecutable|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $executable;
+
+  /**
+   * The mocked views data.
+   *
+   * @var \Drupal\views\ViewsData|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $viewsData;
+
+  protected function setupExecutableAndView() {
+    $this->view = $this->getMockBuilder('Drupal\views\Entity\View')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->executable->storage = $this->view;
+  }
+
+  protected function setupViewsData() {
+    $this->viewsData = $this->getMockBuilder('Drupal\views\ViewsData')
+      ->disableOriginalConstructor()
+      ->getMock();
+  }
+
+}
