diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 8de3f33..d367111 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -225,6 +225,19 @@ field.widget.settings.checkbox:
       type: boolean
       label: 'Use field label instead of the "On value" as label'
 
+field.formatter.settings.boolean:
+  type: mapping
+  mapping:
+    format:
+      type: string
+      label: 'Output format'
+    format_custom_false:
+      type: string
+      label: 'Custom output for FALSE'
+    format_custom_true:
+      type: string
+      label: 'Custom output for TRUE'
+
 field.formatter.settings.string:
   type: mapping
   mapping:
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/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php
index 6860dee..2be5d1d 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/BooleanFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Plugin implementation of the 'boolean' formatter.
@@ -26,11 +27,98 @@ class BooleanFormatter extends FormatterBase {
   /**
    * {@inheritdoc}
    */
+  public static function defaultSettings() {
+    $settings = [];
+
+    // Fallback to field settings by default.
+    $settings['format'] = 'default';
+    $settings['format_custom_false'] = '';
+    $settings['format_custom_true'] = '';
+
+    return $settings;
+  }
+
+  /**
+   * Gets the available format options.
+   *
+   * @return array
+   *   A list of output formats. Each entry is keyed by the machine name of the
+   *   format. The value is an array, of which the first item is the result for
+   *   boolean TRUE, the second is for boolean FALSE.
+   */
+  protected function getOutputFormats() {
+    $formats = [
+      'default' => [$this->getFieldSetting('on_label'), $this->getFieldSetting('off_label')],
+      'yes-no' => [$this->t('Yes'), $this->t('No')],
+      'true-false' => [$this->t('True'), $this->t('False')],
+      'on-off' => [$this->t('On'), $this->t('Off')],
+      'enabled-disabled' => [$this->t('Enabled'), $this->t('Disabled')],
+      'boolean' => [1, 0],
+      'unicode-yes-no' => ['✔', '✖'],
+      'custom' => [$this->t('Custom')],
+    ];
+
+    return $formats;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $form = parent::settingsForm($form, $form_state);
+
+    $formats = [];
+    foreach ($this->getOutputFormats() as $format_name => $format) {
+      $formats[$format_name] = implode(' / ', $format);
+    }
+
+    $form['format'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Output format'),
+      '#default_value' => $this->getSetting('format'),
+      '#options' => $formats,
+    ];
+    $form['format_custom_true'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Custom output for TRUE'),
+      '#default_value' => $this->getSetting('format_custom_true'),
+      '#states' => [
+        'visible' => [
+          'select[name="fields[field_boolean][settings_edit_form][settings][format]"]' => ['value' => 'custom'],
+        ],
+      ],
+    ];
+    $form['format_custom_false'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Custom output for FALSE'),
+      '#default_value' => $this->getSetting('format_custom_false'),
+      '#states' => [
+        'visible' => [
+          'select[name="fields[field_boolean][settings_edit_form][settings][format]"]' => ['value' => 'custom'],
+        ],
+      ],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function viewElements(FieldItemListInterface $items) {
-    $elements = array();
+    $elements = [];
+
+    $formats = $this->getOutputFormats();
 
     foreach ($items as $delta => $item) {
-      $elements[$delta] = array('#markup' => $item->value ? $this->getFieldSetting('on_label') : $this->getFieldSetting('off_label'));
+      $format = $this->getSetting('format');
+
+      if ($format == 'custom') {
+        $elements[$delta] = ['#markup' => $item->value ? $this->getSetting('format_custom_true') : $this->getSetting('format_custom_false')];
+      }
+      else {
+        $elements[$delta] = ['#markup' => $item->value ? $formats[$format][0] : $formats[$format][1]];
+      }
     }
 
     return $elements;
diff --git a/core/modules/action/src/Tests/ActionUninstallTest.php b/core/modules/action/src/Tests/ActionUninstallTest.php
index bec61d1..2735d6e 100644
--- a/core/modules/action/src/Tests/ActionUninstallTest.php
+++ b/core/modules/action/src/Tests/ActionUninstallTest.php
@@ -22,7 +22,7 @@ class ActionUninstallTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('views', 'action');
+  public static $modules = array('field', 'views', 'action');
 
   /**
    * Tests Action uninstall.
diff --git a/core/modules/comment/src/CommentAccessControlHandler.php b/core/modules/comment/src/CommentAccessControlHandler.php
index 0bc58ce..6c365e5 100644
--- a/core/modules/comment/src/CommentAccessControlHandler.php
+++ b/core/modules/comment/src/CommentAccessControlHandler.php
@@ -57,9 +57,14 @@ protected function checkCreateAccess(AccountInterface $account, array $context,
    * {@inheritdoc}
    */
   protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
-    /** @var \Drupal\comment\CommentInterface $entity */
-    $entity = $items->getEntity();
     if ($operation == 'edit') {
+      if (!$items) {
+        // We cannot make a decision about access to edit a field if we don't
+        // have access to the Comment entity.
+        return AccessResult::forbidden();
+      }
+      /** @var \Drupal\comment\CommentInterface $entity */
+      $entity = $items->getEntity();
       // Only users with the "administer comments" permission can edit
       // administrative fields.
       $administrative_fields = array(
@@ -105,14 +110,20 @@ protected function checkFieldAccess($operation, FieldDefinitionInterface $field_
     }
 
     if ($operation == 'view') {
+      $entity = NULL;
+      if ($items) {
+        $entity = $items->getEntity();
+      }
       // Admins can view any fields except hostname, other users need both the
       // "access comments" permission and for the comment to be published. The
       // mail field is hidden from non-admins.
       $admin_access = AccessResult::allowedIf($account->hasPermission('administer comments') && $field_definition->getName() != 'hostname')
         ->cachePerRole();
-      $anonymous_access = AccessResult::allowedIf($account->hasPermission('access comments') && $entity->isPublished() && !in_array($field_definition->getName(), array('mail', 'hostname'), TRUE))
-        ->cacheUntilEntityChanges($entity)
+      $anonymous_access = AccessResult::allowedIf($account->hasPermission('access comments') && (!$entity || $entity->isPublished()) && !in_array($field_definition->getName(), array('mail', 'hostname'), TRUE))
         ->cachePerRole();
+      if ($entity) {
+        $anonymous_access->cacheUntilEntityChanges($entity);
+      }
       return $admin_access->orIf($anonymous_access);
     }
     return parent::checkFieldAccess($operation, $field_definition, $account, $items);
diff --git a/core/modules/comment/src/Tests/Views/CommentRestExportTest.php b/core/modules/comment/src/Tests/Views/CommentRestExportTest.php
new file mode 100644
index 0000000..fdb26dc
--- /dev/null
+++ b/core/modules/comment/src/Tests/Views/CommentRestExportTest.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\Tests\Views\CommentRestExportTest.
+ */
+
+namespace Drupal\comment\Tests\Views;
+use Drupal\Component\Serialization\Json;
+
+/**
+ * Tests a comment rest export view.
+ *
+ * @group comment
+ */
+class CommentRestExportTest extends CommentTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = ['test_comment_rest'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'comment', 'comment_test_views', 'rest', 'hal'];
+
+  protected function setUp() {
+    parent::setUp();
+    // Add another anonymous comment.
+    $comment = array(
+      'uid' => 0,
+      'entity_id' => $this->node_user_commented->id(),
+      'entity_type' => 'node',
+      'field_name' => 'comment',
+      'subject' => 'A lot, apparently',
+      'cid' => '',
+      'pid' => $this->comment->id(),
+      'mail' => 'someone@example.com',
+      'name' => 'bobby tables',
+      'hostname' => 'public.example.com',
+    );
+    $this->comment = entity_create('comment', $comment);
+    $this->comment->save();
+  }
+
+
+  /**
+   * Test comment row.
+   */
+  public function testCommentRestExport() {
+    $this->drupalGet(sprintf('node/%d/comments', $this->node_user_commented->id()), [], ['Accept' => 'application/hal+json']);
+    $this->assertResponse(200);
+    $contents = Json::decode($this->getRawContent());
+    $this->assertEqual($contents[0]['subject'], 'How much wood would a woodchuck chuck');
+    $this->assertEqual($contents[1]['subject'], 'A lot, apparently');
+    $this->assertEqual(count($contents), 2);
+
+    // Ensure field-level access is respected - user shouldn't be able to see
+    // mail or hostname fields.
+    $this->assertNoText('someone@example.com');
+    $this->assertNoText('public.example.com');
+  }
+
+}
diff --git a/core/modules/comment/src/Tests/Views/CommentTestBase.php b/core/modules/comment/src/Tests/Views/CommentTestBase.php
index 306ab24..7d4705f 100644
--- a/core/modules/comment/src/Tests/Views/CommentTestBase.php
+++ b/core/modules/comment/src/Tests/Views/CommentTestBase.php
@@ -79,9 +79,10 @@ protected function setUp() {
       'entity_id' => $this->nodeUserCommented->id(),
       'entity_type' => 'node',
       'field_name' => 'comment',
+      'subject' => 'How much wood would a woodchuck chuck',
       'cid' => '',
       'pid' => '',
-      'node_type' => '',
+      'mail' => 'someone@example.com',
     );
     $this->comment = entity_create('comment', $comment);
     $this->comment->save();
diff --git a/core/modules/node/config/install/views.view.content_recent.yml b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_rest.yml
similarity index 52%
copy from core/modules/node/config/install/views.view.content_recent.yml
copy to core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_rest.yml
index 9456436..1d761ec 100644
--- a/core/modules/node/config/install/views.view.content_recent.yml
+++ b/core/modules/comment/tests/modules/comment_test_views/test_views/views.view.test_comment_rest.yml
@@ -1,16 +1,20 @@
 langcode: en
 status: true
 dependencies:
+  content:
+    - field.storage.comment.comment_body
   module:
     - node
-    - user
-id: content_recent
-label: 'Recent content'
-module: node
-description: 'Recent content.'
-tag: default
-base_table: node
-base_field: nid
+    - comment
+    - field
+    - rest
+id: test_comment_rest
+label: 'Comments by node'
+module: views
+description: ''
+tag: ''
+base_table: comment
+base_field: cid
 core: 8.x
 display:
   default:
@@ -45,72 +49,50 @@ display:
           sort_asc_label: Asc
           sort_desc_label: Desc
       pager:
-        type: some
+        type: full
         options:
           items_per_page: 10
           offset: 0
+          id: 0
+          total_pages: null
+          expose:
+            items_per_page: false
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 25, 50'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: '‹ previous'
+            next: 'next ›'
+            first: '« first'
+            last: 'last »'
+          quantity: 9
       style:
-        type: table
-        options:
-          grouping: {  }
-          row_class: ''
-          default_row_class: true
-          override: true
-          sticky: false
-          caption: ''
-          summary: ''
-          description: ''
-          columns:
-            title: title
-            timestamp: title
-            name: title
-            edit_node: edit_node
-            delete_node: delete_node
-          info:
-            title:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            timestamp:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            name:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: false
-              responsive: ''
-            edit_node:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: true
-              responsive: ''
-            delete_node:
-              sortable: false
-              default_sort_order: asc
-              align: ''
-              separator: ''
-              empty_column: true
-              responsive: ''
-          default: '-1'
-          empty_table: false
+        type: serializer
       row:
         type: fields
+        options:
+          inline: {  }
+          separator: ''
+          hide_empty: false
+          default_field_elements: true
+      relationships:
+        node:
+          id: node
+          table: comment_field_data
+          field: node
+          required: true
+          plugin_id: standard
+          relationship: none
+          group_type: group
+          admin_label: Content
       fields:
-        title:
-          id: title
-          table: node_field_data
-          field: title
+        subject:
+          id: subject
+          table: comment_field_data
+          field: subject
           relationship: none
           group_type: group
           admin_label: ''
@@ -134,8 +116,8 @@ display:
             target: ''
             nl2br: false
             max_length: ''
-            word_boundary: false
-            ellipsis: false
+            word_boundary: true
+            ellipsis: true
             more_link: false
             more_link_text: ''
             more_link_path: ''
@@ -155,15 +137,14 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_node: true
-          plugin_id: node
-          entity_type: node
-          entity_field: title
+          link_to_comment: true
+          link_to_entity: false
+          plugin_id: comment
         name:
           id: name
-          table: users_field_data
+          table: comment_field_data
           field: name
-          relationship: uid
+          relationship: none
           group_type: group
           admin_label: ''
           label: ''
@@ -195,7 +176,7 @@ display:
             trim: false
             preserve_tags: ''
             html: false
-          element_type: div
+          element_type: ''
           element_class: ''
           element_label_type: ''
           element_label_class: ''
@@ -207,17 +188,12 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_user: true
-          overwrite_anonymous: false
-          anonymous_text: ''
-          format_username: true
-          plugin_id: user_name
-          entity_type: user
-          entity_field: name
-        edit_node:
-          id: edit_node
-          table: node
-          field: edit_node
+          link_to_user: false
+          plugin_id: comment_username
+        created:
+          id: created
+          table: comment_field_data
+          field: created
           relationship: none
           group_type: group
           admin_label: ''
@@ -262,13 +238,14 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          text: edit
-          plugin_id: node_link_edit
-          entity_type: node
-        delete_node:
-          id: delete_node
-          table: node
-          field: delete_node
+          date_format: long
+          custom_date_format: ''
+          timezone: ''
+          plugin_id: date
+        comment_body:
+          id: comment_body
+          table: comment__comment_body
+          field: comment_body
           relationship: none
           group_type: group
           admin_label: ''
@@ -313,145 +290,89 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          text: delete
-          plugin_id: node_link_delete
-          entity_type: node
-      filters:
-        status_extra:
-          id: status_extra
-          table: node_field_data
-          field: status_extra
-          relationship: none
-          group_type: group
-          admin_label: ''
-          operator: '='
-          value: false
-          group: 1
-          exposed: false
-          expose:
-            operator_id: '0'
-            label: ''
-            description: ''
-            use_operator: false
-            operator: ''
-            identifier: ''
-            required: false
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-          plugin_id: node_status
-          entity_type: node
-        langcode:
-          id: langcode
-          table: node_field_data
-          field: langcode
-          relationship: none
-          group_type: group
-          admin_label: ''
-          operator: in
-          value:
-            '***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
-          group: 1
-          exposed: false
-          expose:
-            operator_id: ''
-            label: ''
-            description: ''
-            use_operator: false
-            operator: ''
-            identifier: ''
-            required: false
-            remember: false
-            multiple: false
-            remember_roles:
-              authenticated: authenticated
-            reduce: false
-          is_grouped: false
-          group_info:
-            label: ''
-            description: ''
-            identifier: ''
-            optional: true
-            widget: select
-            multiple: false
-            remember: false
-            default_group: All
-            default_group_multiple: {  }
-            group_items: {  }
-          plugin_id: language
-          entity_type: node
-          entity_field: langcode
-      sorts:
-        changed:
-          id: changed
-          table: node_field_data
-          field: changed
-          relationship: none
-          group_type: group
-          admin_label: ''
-          order: DESC
-          exposed: false
-          expose:
-            label: ''
-          granularity: second
-          plugin_id: date
-          entity_type: node
-          entity_field: changed
-      title: 'Recent content'
+          click_sort_column: value
+          type: text_default
+          settings: {  }
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: all
+          delta_offset: '0'
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          plugin_id: field
+      filters: {  }
+      sorts: {  }
       header: {  }
       footer: {  }
-      empty:
-        area_text_custom:
-          id: area_text_custom
-          table: views
-          field: area_text_custom
-          relationship: none
+      empty: {  }
+      arguments:
+        nid:
+          id: nid
+          table: node
+          field: nid
+          relationship: node
           group_type: group
           admin_label: ''
-          empty: true
-          tokenize: false
-          content: 'No content available.'
-          plugin_id: text_custom
-      relationships:
-        uid:
-          id: uid
-          table: node_field_data
-          field: uid
-          relationship: none
-          group_type: group
-          admin_label: author
-          required: true
-          plugin_id: standard
-      arguments: {  }
-      filter_groups:
-        operator: AND
-        groups: {  }
-      use_more: true
-      use_more_always: true
-      use_more_text: More
-      link_display: custom_url
+          default_action: default
+          exception:
+            value: all
+            title_enable: false
+            title: All
+          title_enable: false
+          title: ''
+          default_argument_type: node
+          default_argument_options: {  }
+          default_argument_skip_url: false
+          summary_options:
+            base_path: ''
+            count: true
+            items_per_page: 25
+            override: false
+          summary:
+            sort_order: asc
+            number_of_records: 0
+            format: default_summary
+          specify_validation: false
+          validate:
+            type: none
+            fail: 'not found'
+          validate_options: {  }
+          break_phrase: false
+          not: false
+          plugin_id: numeric
       field_langcode: '***LANGUAGE_language_content***'
       field_langcode_add_to_query: null
-  block_1:
-    display_plugin: block
-    id: block_1
-    display_title: Block
+  rest_export_1:
+    display_plugin: rest_export
+    id: rest_export_1
+    display_title: 'REST export'
     position: 1
     display_options:
-      link_url: admin/content
-      block_category: 'Lists (Views)'
       field_langcode: '***LANGUAGE_language_content***'
       field_langcode_add_to_query: null
+      path: node/%node/comments
+      pager:
+        type: some
+        options:
+          items_per_page: 10
+          offset: 0
+      style:
+        type: serializer
+        options:
+          uses_fields: false
+          formats:
+            json: json
+      row:
+        type: data_field
+        options:
+          field_options:
+            subject:
+              alias: ''
+              raw_output: true
+            name:
+              alias: ''
+              raw_output: true
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/Tests/Boolean/BooleanFormatterTest.php b/core/modules/field/src/Tests/Boolean/BooleanFormatterTest.php
new file mode 100644
index 0000000..5663a58
--- /dev/null
+++ b/core/modules/field/src/Tests/Boolean/BooleanFormatterTest.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field\Tests\Boolean\BooleanFormatterTest.
+ */
+
+namespace Drupal\field\Tests\Boolean;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\simpletest\KernelTestBase;
+
+/**
+ * Tests the boolean formatter.
+ *
+ * @group field
+ */
+class BooleanFormatterTest extends KernelTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['field', 'text', 'entity_test', 'user'];
+
+  /**
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * @var string
+   */
+  protected $bundle;
+
+  /**
+   * @var string
+   */
+  protected $fieldName;
+
+  /**
+   * @var \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+   */
+  protected $display;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Configure the theme system.
+    $this->installConfig(['field']);
+    $this->installEntitySchema('entity_test');
+
+    $this->entityType = 'entity_test';
+    $this->bundle = $this->entityType;
+    $this->fieldName = Unicode::strtolower($this->randomMachineName());
+
+    $field_storage = FieldStorageConfig::create([
+      'field_name' => $this->fieldName,
+      'entity_type' => $this->entityType,
+      'type' => 'boolean',
+    ]);
+    $field_storage->save();
+
+    $instance = FieldConfig::create([
+      'field_storage' => $field_storage,
+      'bundle' => $this->bundle,
+      'label' => $this->randomMachineName(),
+    ]);
+    $instance->save();
+
+    $this->display = entity_get_display($this->entityType, $this->bundle, 'default')
+      ->setComponent($this->fieldName, [
+        'type' => 'boolean',
+        'settings' => [],
+      ]);
+    $this->display->save();
+  }
+
+  /**
+   * Renders fields of a given entity with a given display.
+   *
+   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+   *   The entity object with attached fields to render.
+   * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+   *   The display to render the fields in.
+   *
+   * @return string
+   *   The rendered entity fields.
+   */
+  protected function renderEntityFields(FieldableEntityInterface $entity, EntityViewDisplayInterface $display) {
+    $content = $display->build($entity);
+    $content = $this->render($content);
+    return $content;
+  }
+
+  /**
+   * Tests boolean formatter output.
+   */
+  public function testBooleanFormatter() {
+    $data = [];
+    $data[] = [0, [], 'Off'];
+    $data[] = [1, [], 'On'];
+
+    $format = ['format' => 'enabled-disabled'];
+    $data[] = [0, $format, 'Disabled'];
+    $data[] = [1, $format, 'Enabled'];
+
+    $format = ['format' => 'unicode-yes-no'];
+    $data[] = [1, $format, '✔'];
+    $data[] = [0, $format, '✖'];
+
+    $format = [
+      'format' => 'custom',
+      'format_custom_false' => 'FALSE',
+      'format_custom_true' => 'TRUE'
+    ];
+    $data[] = [0, $format, 'FALSE'];
+    $data[] = [1, $format, 'TRUE'];
+
+    foreach ($data as $test_data) {
+      list($value, $settings, $expected) = $test_data;
+
+      $component = $this->display->getComponent($this->fieldName);
+      $component['settings'] = $settings;
+      $this->display->setComponent($this->fieldName, $component);
+
+      $entity = EntityTest::create([]);
+      $entity->{$this->fieldName}->value = $value;
+
+      // Verify that all HTML is escaped and newlines are retained.
+      $this->renderEntityFields($entity, $this->display);
+      $this->assertRaw($expected);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/core/modules/node/config/install/views.view.content.yml b/core/modules/node/config/install/views.view.content.yml
index 2a44101..7e5005c 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
@@ -222,11 +224,12 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          type: published-notpublished
-          type_custom_true: ''
-          type_custom_false: ''
-          not: ''
-          plugin_id: boolean
+          type: boolean
+          settings:
+            format: published-notpublished
+            format_custom_true: ''
+            format_custom_false: ''
+          plugin_id: field
           entity_type: node
           entity_field: status
         changed:
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/user/config/install/views.view.user_admin_people.yml b/core/modules/user/config/install/views.view.user_admin_people.yml
index ff62509..55f0353 100644
--- a/core/modules/user/config/install/views.view.user_admin_people.yml
+++ b/core/modules/user/config/install/views.view.user_admin_people.yml
@@ -292,11 +292,12 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          type: active-blocked
-          type_custom_true: ''
-          type_custom_false: ''
-          not: '0'
-          plugin_id: boolean
+          plugin_id: field
+          type: boolean
+          settings:
+            format: custom
+            format_custom_true: 'Active'
+            format_custom_false: 'False'
           entity_type: user
           entity_field: status
         roles_target_id:
diff --git a/core/modules/user/user.info.yml b/core/modules/user/user.info.yml
index 45a421a..c4ae5bc 100644
--- a/core/modules/user/user.info.yml
+++ b/core/modules/user/user.info.yml
@@ -6,3 +6,5 @@ version: VERSION
 core: 8.x
 required: true
 configure: user.admin_index
+dependencies:
+  - field
\ No newline at end of file
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index cb30422..c93543e 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/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php
index 025ee02..fa78efe 100644
--- a/core/modules/views/src/Plugin/views/field/Field.php
+++ b/core/modules/views/src/Plugin/views/field/Field.php
@@ -13,7 +13,9 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldConfigInterface;
 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;
@@ -22,6 +24,7 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\RendererInterface;
 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;
@@ -122,6 +125,13 @@ class Field extends FieldPluginBase implements CacheablePluginInterface, MultiIt
   protected $renderer;
 
   /**
+   * The field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypePluginManager;
+
+  /**
    * Constructs a \Drupal\field\Plugin\views\field\Field object.
    *
    * @param array $configuration
@@ -138,15 +148,21 @@ class Field extends FieldPluginBase implements CacheablePluginInterface, MultiIt
    *   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, LanguageManagerInterface $language_manager, RendererInterface $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 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'];
+    }
   }
 
   /**
@@ -159,6 +175,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'),
       $container->get('renderer')
     );
@@ -172,7 +189,7 @@ 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;
@@ -228,48 +245,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);
@@ -287,15 +273,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;
@@ -367,12 +349,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;
@@ -380,12 +358,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.
@@ -400,11 +400,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,
@@ -739,7 +741,7 @@ public function getItems(ResultRow $values) {
     if (!$original_entity) {
       return array();
     }
-    $entity = $this->process_entity($original_entity);
+    $entity = $this->process_entity($values, $original_entity);
     if (!$entity) {
       return array();
     }
@@ -781,13 +783,15 @@ public function getItems(ResultRow $values) {
    * Replaces values with aggregated values if aggregation is enabled.
    * Takes delta settings into account (@todo remove in #1758616).
    *
+   * @param \Drupal\views\ResultRow $values
+   *   The result row object containing the values.
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity to be processed.
    *
    * @return
    *   TRUE if the processing completed successfully, otherwise FALSE.
    */
-  function process_entity(EntityInterface $entity) {
+  function process_entity(ResultRow $values, EntityInterface $entity) {
     $processed_entity = clone $entity;
 
     $langcode = $this->field_langcode($processed_entity);
@@ -950,9 +954,11 @@ public function calculateDependencies() {
     $dependencies = parent::calculateDependencies();
 
     // Add the module providing the configured field storage as a dependency.
-    $dependencies['config'][] = $this->getFieldStorageConfig()->getConfigDependencyName();
+    if (($field_storage = $this->getFieldDefinition()) && $field_storage instanceof FieldConfigInterface) {
+      $dependencies['config'][] = $field_storage->getConfigDependencyName();
+    }
     // Add the module providing the formatter.
-    if ($this->options['type']) {
+    if (!empty($this->options['type'])) {
       $dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
     }
 
@@ -978,4 +984,22 @@ public function getCacheContexts() {
     return $contexts;
   }
 
+  /**
+   * @return \Drupal\Core\Entity\Sql\DefaultTableMapping
+   */
+  protected function getTableMapping() {
+    return $this->entityManager->getStorage($this->definition['entity_type'])->getTableMapping();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue(ResultRow $values, $field = NULL) {
+    if ($field === NULL) {
+      return $this->getEntity($values)->{$this->definition['field_name']}->value;
+    }
+
+    return $this->getEntity($values)->{$this->definition['field_name']}->$field;
+  }
+
 }
diff --git a/core/modules/views/src/Tests/GroupRowsTest.php b/core/modules/views/src/Tests/GroupRowsTest.php
new file mode 100644
index 0000000..bb484a9
--- /dev/null
+++ b/core/modules/views/src/Tests/GroupRowsTest.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\views\Tests\GroupRowsTest.
+ */
+
+namespace Drupal\views\Tests;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+
+/**
+ * Tests the "Display all values in the same row" setting.
+ *
+ * @group views
+ */
+class GroupRowsTest extends ViewTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_group_rows', 'test_ungroup_rows');
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node');
+
+  protected $node_type;
+
+  protected $field_name;
+
+  protected $field_storage;
+
+  protected $field;
+
+  protected function setUp() {
+    parent::setUp();
+
+    // Create content type with unlimited text field.
+    $this->node_type = $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+    // Create the unlimited text field.
+    $this->field_name = 'field_views_testing_group_rows';
+    $this->field_storage = entity_create('field_storage_config', array(
+        'field_name' => $this->field_name,
+        'entity_type' => 'node',
+        'type' => 'text',
+        'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
+      ));
+    $this->field_storage->save();
+
+    // Create an instance of the text field on the content type.
+    $this->field = array(
+      'field_storage' => $this->field_storage,
+      'bundle' => $this->node_type->type,
+    );
+    entity_create('field_config', $this->field)->save();
+
+
+    $edit = array(
+      'title' => $this->randomMachineName(),
+      $this->field_name => array('a', 'b', 'c'),
+    );
+    $this->drupalCreateNode($edit);
+  }
+
+  /**
+   * Testing when "Display all values in the same row" is checked.
+   */
+  function testGroupRows() {
+    $this->drupalGet('test-group-rows');
+    $result = $this->xpath('//div[contains(@class, "views-field-field-views-testing-group-")]/div');
+
+    $rendered_value = array();
+    foreach ($result as $row) {
+      $rendered_value[] = (string) $row[0];
+    }
+    $this->assertEqual(array('a, b, c'), $rendered_value);
+  }
+
+  /**
+   * Testing when "Display all values in the same row" is unchecked.
+   */
+  function testUngroupedRows() {
+    $this->drupalGet('test-ungroup-rows');
+    $result = $this->xpath('//div[contains(@class, "views-field-field-views-testing-group-")]/div');
+    $rendered_value = array();
+    foreach ($result as $row) {
+      $rendered_value[] = (string) $row[0];
+    }
+    $this->assertEqual(array('a', 'b', 'c'), $rendered_value);
+  }
+}
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 37c71e7..b1f34e6 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/ViewUnitTestBase.php b/core/modules/views/src/Tests/ViewUnitTestBase.php
index 86ed262..836d4ee 100644
--- a/core/modules/views/src/Tests/ViewUnitTestBase.php
+++ b/core/modules/views/src/Tests/ViewUnitTestBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Tests;
 
 use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\views\Plugin\views\field\Field;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewsBundle;
 use Drupal\simpletest\KernelTestBase;
@@ -153,7 +154,14 @@ protected function assertIdenticalResultsetHelper($view, $expected_result, $colu
       $row = array();
       foreach ($column_map as $view_column => $expected_column) {
         // The comparison will be done on the string representation of the value.
-        $row[$expected_column] = (string) $value->$view_column;
+        if (isset($value->$view_column)) {
+          $row[$expected_column] = (string) $value->$view_column;
+        }
+        // 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) {
+          $row[$expected_column] = $field->getEntity($value)->{$field->definition['field_name']}->value;
+        }
       }
       $result[$key] = $row;
     }
diff --git a/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php b/core/modules/views/src/Tests/Wizard/ItemsPerPageTest.php
index 946b3c9..054369a 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_group_rows.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml
new file mode 100644
index 0000000..bfb66d0
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_group_rows.yml
@@ -0,0 +1,124 @@
+langcode: und
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_views_testing_group_rows
+  module:
+    - field
+    - node
+    - user
+id: test_group_rows
+label: test_group_rows
+module: views
+description: ''
+tag: ''
+base_table: node
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+      cache:
+        type: none
+      query:
+        type: views_query
+      exposed_form:
+        type: basic
+      pager:
+        options:
+          items_per_page: 10
+        type: full
+      style:
+        type: default
+      row:
+        type: fields
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          plugin_id: node
+        field_views_testing_group_:
+          id: field_views_testing_group_rows
+          table: node__field_views_testing_group_rows
+          field: field_views_testing_group_rows
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: true
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: ''
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: true
+            trim: true
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          settings:
+            link_to_entity: '0'
+          group_column: value
+          group_columns: {  }
+          group_rows: true
+          delta_limit: all
+          delta_offset: '0'
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          plugin_id: field
+      field_langcode: '***LANGUAGE_language_content***'
+      field_langcode_add_to_query: null
+      display_extenders: {  }
+  page_1:
+    display_options:
+      path: test-group-rows
+      field_langcode: '***LANGUAGE_language_content***'
+      field_langcode_add_to_query: null
+      display_extenders: {  }
+    display_plugin: page
+    display_title: 'Page display'
+    id: page_1
+    position: 1
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 802f06f..e07eb18 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,54 @@ display:
           hide_empty: false
           empty_zero: false
           hide_alter_empty: true
-          link_to_node: true
-          plugin_id: node
+          plugin_id: field
           entity_type: node
           entity_field: title
+          type: string
+          settings:
+            link_to_entity: true
+          plugin_id: field
+      filters:
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value:
+            page: page
+          group: 1
+          exposed: false
+          expose:
+            operator_id: ''
+            label: ''
+            description: ''
+            use_operator: false
+            operator: ''
+            identifier: ''
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: false
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: bundle
+          entity_type: node
+          entity_field: type
       group_by: true
       pager:
         type: some
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_ungroup_rows.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_ungroup_rows.yml
new file mode 100644
index 0000000..cdb7303
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_ungroup_rows.yml
@@ -0,0 +1,124 @@
+langcode: und
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_views_testing_group_rows
+  module:
+    - field
+    - node
+    - user
+id: test_ungroup_rows
+label: test_ungroup_rows
+module: views
+description: ''
+tag: ''
+base_table: node
+base_field: nid
+core: 8.x
+display:
+  default:
+    display_plugin: default
+    id: default
+    display_title: Master
+    position: 0
+    display_options:
+      access:
+        type: perm
+      cache:
+        type: none
+      query:
+        type: views_query
+      exposed_form:
+        type: basic
+      pager:
+        options:
+          items_per_page: 10
+        type: full
+      style:
+        type: default
+      row:
+        type: fields
+      fields:
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          entity_type: node
+          entity_field: title
+          plugin_id: node
+        field_views_testing_group_:
+          id: field_views_testing_group_rows
+          table: node__field_views_testing_group_rows
+          field: field_views_testing_group_rows
+          relationship: none
+          group_type: group
+          admin_label: ''
+          label: ''
+          exclude: false
+          alter:
+            alter_text: false
+            text: ''
+            make_link: false
+            path: ''
+            absolute: false
+            external: false
+            replace_spaces: false
+            path_case: none
+            trim_whitespace: true
+            alt: ''
+            rel: ''
+            link_class: ''
+            prefix: ''
+            suffix: ''
+            target: ''
+            nl2br: false
+            max_length: ''
+            word_boundary: true
+            ellipsis: true
+            more_link: false
+            more_link_text: ''
+            more_link_path: ''
+            strip_tags: true
+            trim: true
+            preserve_tags: ''
+            html: false
+          element_type: ''
+          element_class: ''
+          element_label_type: ''
+          element_label_class: ''
+          element_label_colon: false
+          element_wrapper_type: ''
+          element_wrapper_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          click_sort_column: value
+          type: string
+          settings:
+            link_to_entity: '0'
+          group_column: value
+          group_columns: {  }
+          group_rows: false
+          delta_limit: all
+          delta_offset: '0'
+          delta_reversed: false
+          delta_first_last: false
+          multi_type: separator
+          separator: ', '
+          field_api_classes: false
+          plugin_id: field
+      field_langcode: '***LANGUAGE_language_content***'
+      field_langcode_add_to_query: null
+      display_extenders: {  }
+  page_1:
+    display_options:
+      path: test-ungroup-rows
+      field_langcode: '***LANGUAGE_language_content***'
+      field_langcode_add_to_query: null
+      display_extenders: {  }
+    display_plugin: page
+    display_title: 'Page display'
+    id: page_1
+    position: 1
diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php
index c1aac0f..8f6fd5d 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();
+  }
+
+}
diff --git a/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
new file mode 100644
index 0000000..bf95b78
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/field/FieldTest.php
@@ -0,0 +1,546 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\views\Unit\Plugin\field\FieldTest.
+ */
+
+namespace Drupal\Tests\views\Unit\Plugin\field;
+
+use Drupal\Tests\UnitTestCase;
+use Drupal\Tests\views\Unit\Plugin\HandlerTestTrait;
+use Drupal\views\Plugin\views\field\Field;
+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;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $renderer;
+
+  /**
+   * {@inheritdoc}
+   */
+  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->renderer = $this->getMock('Drupal\Core\Render\RendererInterface');
+
+    $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->renderer);
+
+    $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, $this->renderer);
+
+    // 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, $this->renderer);
+
+    // 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, $this->renderer);
+
+    $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, $this->renderer);
+
+    $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, $this->renderer);
+    $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, $this->renderer);
+    $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, $this->renderer);
+    $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, $this->renderer);
+    $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, $this->renderer);
+    $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, $this->renderer);
+    $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'],
+    ];
+  }
+
+}
+
