diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
index b5c236d..f32664a 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
@@ -88,7 +88,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     parent::buildOptionsForm($form, $form_state);
 
     // Get the sorts that apply to our base.
-    $sorts = views_fetch_fields($this->definition['base'], 'sort');
+    $sorts = Views::viewsDataHelper()->fetchFields($this->definition['base'], 'sort');
     foreach ($sorts as $sort_id => $sort) {
       $sort_options[$sort_id] = "$sort[group]: $sort[title]";
     }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
index 3945ae0..e9114d6 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
@@ -831,7 +831,7 @@ protected function default_display_filters_user(array $form, array &$form_state)
       // the base table for the view; the taxonomy vocabulary machine_name, for
       // example, is stored in taxonomy_vocabulary, not taxonomy_term_data.
       module_load_include('inc', 'views_ui', 'admin');
-      $fields = views_fetch_fields($this->base_table, 'filter');
+      $fields = Views::viewsDataHelper()->fetchFields($this->base_table, 'filter');
       if (isset($fields[$this->base_table . '.' . $bundle_key])) {
         $table = $this->base_table;
       }
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
index 1924518..d00a9cf 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
@@ -333,7 +333,7 @@ public function testViewsFetchFields() {
 
     $handler_types = array('field', 'argument', 'filter', 'sort', 'area');
     foreach ($handler_types as $handler_type) {
-      $fields = views_fetch_fields('views_test_data', $handler_type);
+      $fields = $this->container->get('views.views_data_helper')->fetchFields('views_test_data', $handler_type);
       $expected_keys = array_walk($expected[$handler_type], function(&$item) {
         $item = "views_test_data.$item";
       });
@@ -342,7 +342,7 @@ public function testViewsFetchFields() {
 
     // Check for subtype filtering, so header and footer.
     foreach (array('header', 'footer') as $sub_type) {
-      $fields = views_fetch_fields('views_test_data', 'area', FALSE, $sub_type);
+      $fields = $this->container->get('views.views_data_helper')->fetchFields('views_test_data', 'area', FALSE, $sub_type);
 
       $expected_keys = array_walk($expected[$sub_type], function(&$item) {
         $item = "views_test_data.$item";
diff --git a/core/modules/views/lib/Drupal/views/Views.php b/core/modules/views/lib/Drupal/views/Views.php
index 9af207c..e5d2d66 100644
--- a/core/modules/views/lib/Drupal/views/Views.php
+++ b/core/modules/views/lib/Drupal/views/Views.php
@@ -25,6 +25,16 @@ public static function viewsData() {
   }
 
   /**
+   * Returns the views data helper service.
+   *
+   * @return \Drupal\views\ViewsData
+   *   Returns a views data helper object.
+   */
+  public static function viewsDataHelper() {
+    return Drupal::service('views.views_data_helper');
+  }
+
+  /**
    * Returns the view executable factory service.
    *
    * @return \Drupal\views\ViewExecutableFactory
diff --git a/core/modules/views/lib/Drupal/views/ViewsDataHelper.php b/core/modules/views/lib/Drupal/views/ViewsDataHelper.php
new file mode 100644
index 0000000..feb249f
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewsDataHelper.php
@@ -0,0 +1,188 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewsDataHelper.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Defines a helper class for stuff related to views data.
+ */
+class ViewsDataHelper {
+
+  /**
+   * The views data object, containing the cached information.
+   *
+   * @var \Drupal\views\ViewsData
+   */
+  protected $data;
+
+  /**
+   * A prepared list of all fields, keyed by base_table and handler type.
+   *
+   * @param array
+   */
+  protected $fields;
+
+  /**
+   * Constructs a ViewsData object.
+   *
+   * @param \Drupal\views\ViewsData $views_data
+   *   The views data object, containing the cached table information.
+   */
+  public function __construct(ViewsData $views_data) {
+    $this->data = $views_data;
+  }
+
+  /**
+   * Fetches a list of all fields available for a given base type.
+   *
+   * @param (array|string) $base
+   *   A list or a single base_table, for example node.
+   * @param string $type
+   *   The handler type, for example field or filter.
+   * @param bool $grouping
+   *   Should the result grouping by its 'group' label.
+   * @param string $sub_type
+   *   An optional sub type. E.g. Allows making an area plugin available for
+   *   header only, instead of header, footer, and empty regions.
+   *
+   * @return array
+   *   A keyed array of in the form of 'base_table' => 'Description'.
+   */
+  public function fetchFields($base, $type, $grouping = FALSE, $sub_type = NULL) {
+    if (!$this->fields) {
+      $data = $this->data->get();
+      // This constructs this ginormous multi dimensional array to
+      // collect the important data about fields. In the end,
+      // the structure looks a bit like this (using nid as an example)
+      // $strings['nid']['filter']['title'] = 'string'.
+      //
+      // This is constructed this way because the above referenced strings
+      // can appear in different places in the actual data structure so that
+      // the data doesn't have to be repeated a lot. This essentially lets
+      // each field have a cheap kind of inheritance.
+
+      foreach ($data as $table => $table_data) {
+        $bases = array();
+        $strings = array();
+        $skip_bases = array();
+        foreach ($table_data as $field => $info) {
+          // Collect table data from this table
+          if ($field == 'table') {
+            // calculate what tables this table can join to.
+            if (!empty($info['join'])) {
+              $bases = array_keys($info['join']);
+            }
+            // And it obviously joins to itself.
+            $bases[] = $table;
+            continue;
+          }
+          foreach (array('field', 'sort', 'filter', 'argument', 'relationship', 'area') as $key) {
+            if (!empty($info[$key])) {
+              if ($grouping && !empty($info[$key]['no group by'])) {
+                continue;
+              }
+              if ($sub_type && isset($info[$key]['sub_type']) && (!in_array($sub_type, (array) $info[$key]['sub_type']))) {
+                continue;
+              }
+              if (!empty($info[$key]['skip base'])) {
+                foreach ((array) $info[$key]['skip base'] as $base_name) {
+                  $skip_bases[$field][$key][$base_name] = TRUE;
+                }
+              }
+              elseif (!empty($info['skip base'])) {
+                foreach ((array) $info['skip base'] as $base_name) {
+                  $skip_bases[$field][$key][$base_name] = TRUE;
+                }
+              }
+              foreach (array('title', 'group', 'help', 'base', 'aliases') as $string) {
+                // First, try the lowest possible level
+                if (!empty($info[$key][$string])) {
+                  $strings[$field][$key][$string] = $info[$key][$string];
+                }
+                // Then try the field level
+                elseif (!empty($info[$string])) {
+                  $strings[$field][$key][$string] = $info[$string];
+                }
+                // Finally, try the table level
+                elseif (!empty($table_data['table'][$string])) {
+                  $strings[$field][$key][$string] = $table_data['table'][$string];
+                }
+                else {
+                  if ($string != 'base' && $string != 'base') {
+                    $strings[$field][$key][$string] = t("Error: missing @component", array('@component' => $string));
+                  }
+                }
+              }
+            }
+          }
+        }
+        foreach ($bases as $base_name) {
+          foreach ($strings as $field => $field_strings) {
+            foreach ($field_strings as $type_name => $type_strings) {
+              if (empty($skip_bases[$field][$type_name][$base_name])) {
+                $fields[$base_name][$type_name]["$table.$field"] = $type_strings;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // If we have an array of base tables available, go through them
+    // all and add them together. Duplicate keys will be lost and that's
+    // Just Fine.
+    if (is_array($base)) {
+      $strings = array();
+      foreach ($base as $base_table) {
+        if (isset($fields[$base_table][$type])) {
+          $strings += $fields[$base_table][$type];
+        }
+      }
+      uasort($strings, array('self', 'fetchedFieldSort'));
+      return $strings;
+    }
+
+    if (isset($fields[$base][$type])) {
+      uasort($fields[$base][$type], array($this, 'fetchedFieldSort'));
+      return $fields[$base][$type];
+    }
+    return array();
+  }
+
+  /**
+   * Sort function for fetched fields.
+   *
+   * @param array $a
+   *   First item for comparison. The compared items should be associative arrays
+   *   that include a 'group' and a 'title' key.
+   * @param array $b
+   *   Second item for comparison.
+   *
+   * @return int
+   *   Returns -1 if $a comes before $b, 1 other way round and 0 if it cannot be
+   *   decided.
+   */
+  protected static function fetchedFieldSort($a, $b) {
+    $a_group = Unicode::strtolower($a['group']);
+    $b_group = Unicode::strtolower($b['group']);
+    if ($a_group != $b_group) {
+      return $a_group < $b_group ? -1 : 1;
+    }
+
+    $a_title = Unicode::strtolower($a['title']);
+    $b_title = Unicode::strtolower($b['title']);
+    if ($a_title != $b_title) {
+      return $a_title < $b_title ? -1 : 1;
+    }
+
+    return 0;
+  }
+
+}
+
diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml
index 1629da5..59d5e10 100644
--- a/core/modules/views/views.services.yml
+++ b/core/modules/views/views.services.yml
@@ -59,6 +59,9 @@ services:
   views.views_data:
     class: Drupal\views\ViewsData
     arguments: ['@cache.views_info', '@config.factory', '@module_handler']
+  views.views_data_helper:
+    class: Drupal\views\ViewsDataHelper
+    arguments: ['@views.views_data']
   views.executable:
     class: Drupal\views\ViewExecutableFactory
   views.analyzer:
diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc
index a723854..42e8b30 100644
--- a/core/modules/views_ui/admin.inc
+++ b/core/modules/views_ui/admin.inc
@@ -408,141 +408,6 @@ function views_ui_build_form_url($form_state) {
   return $url;
 }
 
-function _views_sort_types($a, $b) {
-  $a_group = drupal_strtolower($a['group']);
-  $b_group = drupal_strtolower($b['group']);
-  if ($a_group != $b_group) {
-    return $a_group < $b_group ? -1 : 1;
-  }
-
-  $a_title = drupal_strtolower($a['title']);
-  $b_title = drupal_strtolower($b['title']);
-  if ($a_title != $b_title) {
-    return $a_title < $b_title ? -1 : 1;
-  }
-
-  return 0;
-}
-
-/**
- * Fetch a list of all fields available for a given base type.
- *
- * @param (array|string) $base
- *   A list or a single base_table, for example node.
- * @param string $type
- *   The handler type, for example field or filter.
- * @param bool $grouping
- *   Should the result grouping by its 'group' label.
- * @param string $sub_type
- *   An optional sub type. E.g. Allows making an area plugin available for
- *   header only, instead of header, footer, and empty regions.
- *
- * @return array
- *   A keyed array of in the form of 'base_table' => 'Description'.
- */
-function views_fetch_fields($base, $type, $grouping = FALSE, $sub_type = NULL) {
-  static $fields = array();
-  if (empty($fields)) {
-    $data = Views::viewsData()->get();
-    $start = microtime(TRUE);
-    // This constructs this ginormous multi dimensional array to
-    // collect the important data about fields. In the end,
-    // the structure looks a bit like this (using nid as an example)
-    // $strings['nid']['filter']['title'] = 'string'.
-    //
-    // This is constructed this way because the above referenced strings
-    // can appear in different places in the actual data structure so that
-    // the data doesn't have to be repeated a lot. This essentially lets
-    // each field have a cheap kind of inheritance.
-
-    foreach ($data as $table => $table_data) {
-      $bases = array();
-      $strings = array();
-      $skip_bases = array();
-      foreach ($table_data as $field => $info) {
-        // Collect table data from this table
-        if ($field == 'table') {
-          // calculate what tables this table can join to.
-          if (!empty($info['join'])) {
-            $bases = array_keys($info['join']);
-          }
-          // And it obviously joins to itself.
-          $bases[] = $table;
-          continue;
-        }
-        foreach (array('field', 'sort', 'filter', 'argument', 'relationship', 'area') as $key) {
-          if (!empty($info[$key])) {
-            if ($grouping && !empty($info[$key]['no group by'])) {
-              continue;
-            }
-            if ($sub_type && isset($info[$key]['sub_type']) && (!in_array($sub_type, (array) $info[$key]['sub_type']))) {
-              continue;
-            }
-            if (!empty($info[$key]['skip base'])) {
-              foreach ((array) $info[$key]['skip base'] as $base_name) {
-                $skip_bases[$field][$key][$base_name] = TRUE;
-              }
-            }
-            elseif (!empty($info['skip base'])) {
-              foreach ((array) $info['skip base'] as $base_name) {
-                $skip_bases[$field][$key][$base_name] = TRUE;
-              }
-            }
-            foreach (array('title', 'group', 'help', 'base', 'aliases') as $string) {
-              // First, try the lowest possible level
-              if (!empty($info[$key][$string])) {
-                $strings[$field][$key][$string] = $info[$key][$string];
-              }
-              // Then try the field level
-              elseif (!empty($info[$string])) {
-                $strings[$field][$key][$string] = $info[$string];
-              }
-              // Finally, try the table level
-              elseif (!empty($table_data['table'][$string])) {
-                $strings[$field][$key][$string] = $table_data['table'][$string];
-              }
-              else {
-                if ($string != 'base' && $string != 'base') {
-                  $strings[$field][$key][$string] = t("Error: missing @component", array('@component' => $string));
-                }
-              }
-            }
-          }
-        }
-      }
-      foreach ($bases as $base_name) {
-        foreach ($strings as $field => $field_strings) {
-          foreach ($field_strings as $type_name => $type_strings) {
-            if (empty($skip_bases[$field][$type_name][$base_name])) {
-              $fields[$base_name][$type_name]["$table.$field"] = $type_strings;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  // If we have an array of base tables available, go through them
-  // all and add them together. Duplicate keys will be lost and that's
-  // Just Fine.
-  if (is_array($base)) {
-    $strings = array();
-    foreach ($base as $base_table) {
-      if (isset($fields[$base_table][$type])) {
-        $strings += $fields[$base_table][$type];
-      }
-    }
-    uasort($strings, '_views_sort_types');
-    return $strings;
-  }
-
-  if (isset($fields[$base][$type])) {
-    uasort($fields[$base][$type], '_views_sort_types');
-    return $fields[$base][$type];
-  }
-  return array();
-}
-
 /**
  * #process callback for a button; determines if a button is the form's triggering element.
  *
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php
index 1c8d13e..a1907ab 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/AddItem.php
@@ -9,6 +9,7 @@
 
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewStorageInterface;
+use Drupal\views\Views;
 
 /**
  * Provides a form for adding an item in the Views UI.
@@ -81,7 +82,7 @@ public function buildForm(array $form, array &$form_state) {
 
     // Figure out all the base tables allowed based upon what the relationships provide.
     $base_tables = $executable->getBaseTables();
-    $options = views_fetch_fields(array_keys($base_tables), $type, $display->useGroupBy(), $form_state['type']);
+    $options = Views::viewsDataHelper()->fetchFields(array_keys($base_tables), $type, $display->useGroupBy(), $form_state['type']);
 
     if (!empty($options)) {
       $form['override']['controls'] = array(
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php
index e35cee0..04ed826 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/Form/Ajax/ConfigItem.php
@@ -105,7 +105,7 @@ public function buildForm(array $form, array &$form_state) {
           // If this relationship is valid for this type, add it to the list.
           $data = Views::viewsData()->get($relationship['table']);
           $base = $data[$relationship['field']]['relationship']['base'];
-          $base_fields = views_fetch_fields($base, $form_state['type'], $executable->display_handler->useGroupBy());
+          $base_fields = Views::viewsDataHelper()->fetchFields($base, $form_state['type'], $executable->display_handler->useGroupBy());
           if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
             $relationship_handler->init($executable, $executable->display_handler, $relationship);
             $relationship_options[$relationship['id']] = $relationship_handler->adminLabel();
@@ -115,7 +115,7 @@ public function buildForm(array $form, array &$form_state) {
         if (!empty($relationship_options)) {
           // Make sure the existing relationship is even valid. If not, force
           // it to none.
-          $base_fields = views_fetch_fields($view->get('base_table'), $form_state['type'], $executable->display_handler->useGroupBy());
+          $base_fields = Views::viewsDataHelper()->fetchFields($view->get('base_table'), $form_state['type'], $executable->display_handler->useGroupBy());
           if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
             $relationship_options = array_merge(array('none' => t('Do not use a relationship')), $relationship_options);
           }
