diff --git a/modules/search_api_db/src/Plugin/search_api/backend/Database.php b/modules/search_api_db/src/Plugin/search_api/backend/Database.php
index a1c078bc..1f9e571d 100644
--- a/modules/search_api_db/src/Plugin/search_api/backend/Database.php
+++ b/modules/search_api_db/src/Plugin/search_api/backend/Database.php
@@ -27,6 +27,7 @@
 use Drupal\search_api\Query\ConditionGroupInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\SearchApiException;
+use Drupal\search_api\Utility\DataTypeHelper;
 use Drupal\search_api\Utility\Utility;
 use Drupal\search_api_autocomplete\Entity\SearchApiAutocompleteSearch;
 use Drupal\search_api_autocomplete\Suggestion;
@@ -139,6 +140,13 @@ class Database extends BackendPluginBase implements PluginFormInterface {
   protected $dateFormatter;
 
   /**
+   * The data type helper.
+   *
+   * @var \Drupal\search_api\Utility\DataTypeHelper|null
+   */
+  protected $dataTypeHelper;
+
+  /**
    * The keywords ignored during the current search query.
    *
    * @var array
@@ -184,6 +192,8 @@ public static function create(ContainerInterface $container, array $configuratio
     $backend->setDataTypePluginManager($container->get('plugin.manager.search_api.data_type'));
     $backend->setLogger($container->get('logger.channel.search_api_db'));
     $backend->setKeyValueStore($container->get('keyvalue')->get(self::INDEXES_KEY_VALUE_STORE_ID));
+    $backend->setDateFormatter($container->get('date.formatter'));
+    $backend->setDataTypeHelper($container->get('search_api.data_type_helper'));
 
     // For a new backend plugin, the database might not be set yet. In that case
     // we of course also don't need a DBMS compatibility handler.
@@ -326,6 +336,29 @@ public function setDateFormatter(DateFormatterInterface $date_formatter) {
   }
 
   /**
+   * Retrieves the data type helper.
+   *
+   * @return \Drupal\search_api\Utility\DataTypeHelper
+   *   The data type helper.
+   */
+  public function getDataTypeHelper() {
+    return $this->dataTypeHelper ?: \Drupal::service('search_api.data_type_helper');
+  }
+
+  /**
+   * Sets the data type helper.
+   *
+   * @param \Drupal\search_api\Utility\DataTypeHelper $data_type_helper
+   *   The new data type helper.
+   *
+   * @return $this
+   */
+  public function setDataTypeHelper(DataTypeHelper $data_type_helper) {
+    $this->dataTypeHelper = $data_type_helper;
+    return $this;
+  }
+
+  /**
    * Sets the DBMS compatibility handler.
    *
    * @param \Drupal\search_api_db\DatabaseCompatibility\DatabaseCompatibilityHandlerInterface $handler
@@ -833,7 +866,8 @@ protected function fieldsUpdated(IndexInterface $index) {
       $denormalized_table = $db_info['index_table'];
 
       foreach ($fields as $field_id => $field) {
-        if (!isset($text_table) && Utility::isTextType($field['type'])) {
+        $was_text_type = $this->getDataTypeHelper()->isTextType($field['type']);
+        if (!isset($text_table) && $was_text_type) {
           // Stash the shared text table name for the index.
           $text_table = $field['table'];
         }
@@ -849,7 +883,8 @@ protected function fieldsUpdated(IndexInterface $index) {
         $fields[$field_id]['type'] = $new_type;
         $fields[$field_id]['boost'] = $new_fields[$field_id]->getBoost();
         if ($old_type != $new_type) {
-          if ($old_type == 'text' || $new_type == 'text') {
+          $is_text_type = $this->getDataTypeHelper()->isTextType($new_type);
+          if ($was_text_type || $is_text_type) {
             // A change in fulltext status necessitates completely clearing the
             // index.
             $reindex = TRUE;
@@ -874,7 +909,7 @@ protected function fieldsUpdated(IndexInterface $index) {
             $reindex = TRUE;
           }
         }
-        elseif ($new_type == 'text' && $field['boost'] != $new_fields[$field_id]->getBoost()) {
+        elseif ($was_text_type && $field['boost'] != $new_fields[$field_id]->getBoost()) {
           if (!$reindex) {
             // If there was a non-zero boost set previously, we can just update
             // all scores with a single UPDATE query. Otherwise, no way around
@@ -898,7 +933,7 @@ protected function fieldsUpdated(IndexInterface $index) {
           ->fieldExists($field['table'], 'value');
         $denormalized_storage_exists = $this->database->schema()
           ->fieldExists($denormalized_table, $field['column']);
-        if (!Utility::isTextType($field['type']) && !$storage_exists) {
+        if (!$was_text_type && !$storage_exists) {
           $db = array(
             'table' => $field['table'],
           );
@@ -921,7 +956,7 @@ protected function fieldsUpdated(IndexInterface $index) {
       foreach ($new_fields as $field_id => $field) {
         $reindex = TRUE;
         $fields[$field_id] = array();
-        if (Utility::isTextType($field->getType())) {
+        if ($this->getDataTypeHelper()->isTextType($field->getType())) {
           if (!isset($text_table)) {
             // If we have not encountered a text table, assign a name for it.
             $text_table = $this->findFreeTable($prefix . '_', 'text');
@@ -1006,7 +1041,7 @@ protected function fieldsUpdated(IndexInterface $index) {
    *   The table which stores the denormalized data for this field.
    */
   protected function removeFieldStorage($name, $field, $index_table) {
-    if (Utility::isTextType($field['type'])) {
+    if ($this->getDataTypeHelper()->isTextType($field['type'])) {
       // Remove data from the text table.
       $this->database->delete($field['table'])
         ->condition('field_name', self::getTextFieldName($name))
@@ -1170,7 +1205,7 @@ protected function indexItem(IndexInterface $index, ItemInterface $item) {
           $db_info['field_tables'][$field_id]['multi-valued'] = TRUE;
         }
 
-        if (Utility::isTextType($type)) {
+        if ($this->getDataTypeHelper()->isTextType($type)) {
           // Remember the text table the first time we encounter it.
           if (!isset($text_table)) {
             $text_table = $table;
@@ -1326,7 +1361,7 @@ protected static function getTextFieldName($name) {
   protected function convert($value, $type, $original_type, IndexInterface $index) {
     if (!isset($value)) {
       // For text fields, we have to return an array even if the value is NULL.
-      return Utility::isTextType($type) ? array() : NULL;
+      return $this->getDataTypeHelper()->isTextType($type) ? array() : NULL;
     }
     switch ($type) {
       case 'text':
@@ -1534,7 +1569,7 @@ public function search(QueryInterface $query) {
       $result = $db_query->execute();
 
       foreach ($result as $row) {
-        $item = Utility::createItem($index, $row->item_id);
+        $item = $this->getFieldsHelper()->createItem($index, $row->item_id);
         $item->setScore($row->score / self::SCORE_MULTIPLIER);
         $results->addResultItem($item);
       }
@@ -1597,7 +1632,7 @@ protected function createDbQuery(QueryInterface $query, array $fields) {
           if (!isset($fields[$name])) {
             throw new SearchApiException("Unknown field '$name' specified as search target.");
           }
-          if (!Utility::isTextType($fields[$name]['type'])) {
+          if (!$this->getDataTypeHelper()->isTextType($fields[$name]['type'])) {
             $types = $this->getDataTypePluginManager()->getInstances();
             $type = $types[$fields[$name]['type']]->label();
             throw new SearchApiException("Cannot perform fulltext search on field '$name' of type '$type'.");
@@ -2089,7 +2124,7 @@ protected function createDbCondition(ConditionGroupInterface $conditions, array
             $db_condition->condition($column, $value, $operator);
           }
         }
-        elseif (Utility::isTextType($field_info['type'])) {
+        elseif ($this->getDataTypeHelper()->isTextType($field_info['type'])) {
           $keys = $this->prepareKeys($value);
           if (!isset($keys)) {
             continue;
@@ -2301,7 +2336,7 @@ protected function getFacets(QueryInterface $query, SelectInterface $db_query) {
 
       // If "Include missing facet" is disabled, we use an INNER JOIN and add IS
       // NOT NULL for shared tables.
-      $is_text_type = Utility::isTextType($field['type']);
+      $is_text_type = $this->getDataTypeHelper()->isTextType($field['type']);
       $alias = $this->getTableAlias($field, $select, TRUE, $facet['missing'] ? 'leftJoin' : 'innerJoin');
       $select->addField($alias, $is_text_type ? 'word' : 'value', 'value');
       if ($is_text_type) {
@@ -2515,7 +2550,7 @@ public function getAutocompleteSuggestions(QueryInterface $query, SearchApiAutoc
       /** @var \Drupal\Core\Database\Query\SelectInterface|null $word_query */
       $word_query = NULL;
       foreach ($fulltext_fields as $field) {
-        if (!isset($fields[$field]) || !Utility::isTextType($fields[$field]['type'])) {
+        if (!isset($fields[$field]) || !$this->getDataTypeHelper()->isTextType($fields[$field]['type'])) {
           continue;
         }
         $field_query = $this->database->select($fields[$field]['table'], 't');
diff --git a/src/Backend/BackendPluginBase.php b/src/Backend/BackendPluginBase.php
index 401d640a..6c49fdbb 100644
--- a/src/Backend/BackendPluginBase.php
+++ b/src/Backend/BackendPluginBase.php
@@ -10,7 +10,7 @@
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\Plugin\ConfigurablePluginBase;
 use Drupal\search_api\ServerInterface;
-use Drupal\search_api\Utility\Utility;
+use Drupal\search_api\Utility\FieldsHelper;
 
 /**
  * Defines a base class for backend plugins.
@@ -60,6 +60,13 @@
   protected $serverId;
 
   /**
+   * The fields helper.
+   *
+   * @var \Drupal\search_api\Utility\FieldsHelper|null
+   */
+  protected $fieldsHelper;
+
+  /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
@@ -71,6 +78,29 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
   }
 
   /**
+   * Retrieves the fields helper.
+   *
+   * @return \Drupal\search_api\Utility\FieldsHelper
+   *   The fields helper.
+   */
+  public function getFieldsHelper() {
+    return $this->fieldsHelper ?: \Drupal::service('search_api.fields_helper');
+  }
+
+  /**
+   * Sets the fields helper.
+   *
+   * @param \Drupal\search_api\Utility\FieldsHelper $fields_helper
+   *   The new fields helper.
+   *
+   * @return $this
+   */
+  public function setFieldsHelper(FieldsHelper $fields_helper) {
+    $this->fieldsHelper = $fields_helper;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getServer() {
@@ -198,9 +228,12 @@ protected function getSpecialFields(IndexInterface $index, ItemInterface $item =
       'type' => 'string',
       'original type' => 'string',
     );
-    $fields['search_api_id'] = Utility::createField($index, 'search_api_id', $field_info);
-    $fields['search_api_datasource'] = Utility::createField($index, 'search_api_datasource', $field_info);
-    $fields['search_api_language'] = Utility::createField($index, 'search_api_language', $field_info);
+    $fields['search_api_id'] = $this->getFieldsHelper()
+      ->createField($index, 'search_api_id', $field_info);
+    $fields['search_api_datasource'] = $this->getFieldsHelper()
+      ->createField($index, 'search_api_datasource', $field_info);
+    $fields['search_api_language'] = $this->getFieldsHelper()
+      ->createField($index, 'search_api_language', $field_info);
 
     if ($item) {
       $fields['search_api_id']->setValues(array($item->getId()));
diff --git a/src/DataType/DataTypeInterface.php b/src/DataType/DataTypeInterface.php
index 827ab2ef..ddbd4144 100644
--- a/src/DataType/DataTypeInterface.php
+++ b/src/DataType/DataTypeInterface.php
@@ -45,8 +45,6 @@ public function getValue($value);
    *
    * @return string
    *   The fallback default data type.
-   *
-   * @see \Drupal\search_api\Utility::getDefaultDataTypes()
    */
   public function getFallbackType();
 
diff --git a/src/Form/IndexAddFieldsForm.php b/src/Form/IndexAddFieldsForm.php
index 2de567f2..ec1f0087 100644
--- a/src/Form/IndexAddFieldsForm.php
+++ b/src/Form/IndexAddFieldsForm.php
@@ -16,6 +16,7 @@
 use Drupal\search_api\Datasource\DatasourceInterface;
 use Drupal\search_api\Processor\ConfigurablePropertyInterface;
 use Drupal\search_api\Processor\ProcessorPropertyInterface;
+use Drupal\search_api\Utility\DataTypeHelperInterface;
 use Drupal\search_api\Utility\FieldsHelperInterface;
 use Drupal\search_api\Utility\Utility;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -35,6 +36,13 @@ class IndexAddFieldsForm extends EntityForm {
   protected $fieldsHelper;
 
   /**
+   * The data type helper.
+   *
+   * @var \Drupal\search_api\Utility\DataTypeHelperInterface|null
+   */
+  protected $dataTypeHelper;
+
+  /**
    * The index for which the fields are configured.
    *
    * @var \Drupal\search_api\IndexInterface
@@ -79,6 +87,8 @@ public function getBaseFormId() {
    *   The entity manager.
    * @param \Drupal\search_api\Utility\FieldsHelperInterface $fields_helper
    *   The fields helper.
+   * @param \Drupal\search_api\Utility\DataTypeHelperInterface $data_type_helper
+   *   The data type helper.
    * @param \Drupal\Core\Render\RendererInterface $renderer
    *   The renderer to use.
    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
@@ -86,9 +96,10 @@ public function getBaseFormId() {
    * @param array $parameters
    *   The parameters for this page request.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, FieldsHelperInterface $fields_helper, RendererInterface $renderer, DateFormatterInterface $date_formatter, array $parameters) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, FieldsHelperInterface $fields_helper, DataTypeHelperInterface $data_type_helper, RendererInterface $renderer, DateFormatterInterface $date_formatter, array $parameters) {
     $this->entityTypeManager = $entity_type_manager;
     $this->fieldsHelper = $fields_helper;
+    $this->dataTypeHelper = $data_type_helper;
     $this->renderer = $renderer;
     $this->dateFormatter = $date_formatter;
     $this->parameters = $parameters;
@@ -100,12 +111,13 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Fie
   public static function create(ContainerInterface $container) {
     $entity_type_manager = $container->get('entity_type.manager');
     $fields_helper = $container->get('search_api.fields_helper');
+    $data_type_helper = $container->get('search_api.data_type_helper');
     $renderer = $container->get('renderer');
     $date_formatter = $container->get('date.formatter');
     $request_stack = $container->get('request_stack');
     $parameters = $request_stack->getCurrentRequest()->query->all();
 
-    return new static($entity_type_manager, $fields_helper, $renderer, $date_formatter, $parameters);
+    return new static($entity_type_manager, $fields_helper, $data_type_helper, $renderer, $date_formatter, $parameters);
   }
 
   /**
@@ -265,7 +277,7 @@ protected function getPropertiesList(array $properties, $active_property_path, U
       list($active_item, $active_property_path) = explode(':', $active_property_path, 2) + array(1 => '');
     }
 
-    $type_mapping = Utility::getFieldTypeMapping();
+    $type_mapping = $this->dataTypeHelper->getFieldTypeMapping();
 
     $query_base = $base_url->getOption('query');
     foreach ($properties as $key => $property) {
diff --git a/src/Form/IndexFieldsForm.php b/src/Form/IndexFieldsForm.php
index a3128072..b0574b12 100644
--- a/src/Form/IndexFieldsForm.php
+++ b/src/Form/IndexFieldsForm.php
@@ -12,7 +12,8 @@
 use Drupal\search_api\DataType\DataTypePluginManager;
 use Drupal\search_api\Processor\ConfigurablePropertyInterface;
 use Drupal\search_api\UnsavedConfigurationInterface;
-use Drupal\search_api\Utility\Utility;
+use Drupal\search_api\Utility\DataTypeHelperInterface;
+use Drupal\search_api\Utility\FieldsHelperInterface;
 use Drupal\user\SharedTempStoreFactory;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -45,6 +46,20 @@ class IndexFieldsForm extends EntityForm {
   protected $dataTypePluginManager;
 
   /**
+   * The data type helper.
+   *
+   * @var \Drupal\search_api\Utility\DataTypeHelperInterface|null
+   */
+  protected $dataTypeHelper;
+
+  /**
+   * The fields helper.
+   *
+   * @var \Drupal\search_api\Utility\FieldsHelperInterface|null
+   */
+  protected $fieldsHelper;
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -71,13 +86,19 @@ public function getBaseFormId() {
    *   The renderer to use.
    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
    *   The date formatter.
+   * @param \Drupal\search_api\Utility\DataTypeHelperInterface $data_type_helper
+   *   The data type helper.
+   * @param \Drupal\search_api\Utility\FieldsHelperInterface $fields_helper
+   *   The fields helper.
    */
-  public function __construct(SharedTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, DataTypePluginManager $data_type_plugin_manager, RendererInterface $renderer, DateFormatterInterface $date_formatter) {
+  public function __construct(SharedTempStoreFactory $temp_store_factory, EntityTypeManagerInterface $entity_type_manager, DataTypePluginManager $data_type_plugin_manager, RendererInterface $renderer, DateFormatterInterface $date_formatter, DataTypeHelperInterface $data_type_helper, FieldsHelperInterface $fields_helper) {
     $this->tempStore = $temp_store_factory->get('search_api_index');
     $this->entityTypeManager = $entity_type_manager;
     $this->dataTypePluginManager = $data_type_plugin_manager;
     $this->renderer = $renderer;
     $this->dateFormatter = $date_formatter;
+    $this->dataTypeHelper = $data_type_helper;
+    $this->fieldsHelper = $fields_helper;
   }
 
   /**
@@ -89,18 +110,10 @@ public static function create(ContainerInterface $container) {
     $data_type_plugin_manager = $container->get('plugin.manager.search_api.data_type');
     $renderer = $container->get('renderer');
     $date_formatter = $container->get('date.formatter');
+    $data_type_helper = $container->get('search_api.data_type_helper');
+    $fields_helper = $container->get('search_api.fields_helper');
 
-    return new static($temp_store_factory, $entity_type_manager, $data_type_plugin_manager, $renderer, $date_formatter);
-  }
-
-  /**
-   * Retrieves the data type plugin manager.
-   *
-   * @return \Drupal\search_api\DataType\DataTypePluginManager
-   *   The data type plugin manager.
-   */
-  public function getDataTypePluginManager() {
-    return $this->dataTypePluginManager;
+    return new static($temp_store_factory, $entity_type_manager, $data_type_plugin_manager, $renderer, $date_formatter, $data_type_helper, $fields_helper);
   }
 
   /**
@@ -134,7 +147,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Build the data type table.
     $instances = $this->dataTypePluginManager->getInstances();
-    $fallback_mapping = Utility::getDataTypeFallbackMapping($index);
+    $fallback_mapping = $this->dataTypeHelper
+      ->getDataTypeFallbackMapping($index);
 
     $data_types = array();
     foreach ($instances as $name => $type) {
@@ -170,9 +184,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    *   The build structure.
    */
   protected function buildFieldsTable(array $fields) {
-    $data_type_plugin_manager = $this->getDataTypePluginManager();
-    $types = $data_type_plugin_manager->getInstancesOptions();
-    $fallback_types = Utility::getDataTypeFallbackMapping($this->entity);
+    $types = $this->dataTypePluginManager->getInstancesOptions();
+    $fallback_types = $this->dataTypeHelper
+      ->getDataTypeFallbackMapping($this->entity);
 
     // If one of the unsupported types is actually used by the index, show a
     // warning.
@@ -187,7 +201,7 @@ protected function buildFieldsTable(array $fields) {
 
     $fulltext_types = array('text');
     // Add all data types with fallback "text" to fulltext types as well.
-    foreach ($data_type_plugin_manager->getInstances() as $id => $type) {
+    foreach ($this->dataTypePluginManager->getInstances() as $id => $type) {
       if ($type->getFallbackType() == 'text') {
         $fulltext_types[] = $id;
       }
@@ -339,7 +353,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
       $new_ids[$new_id][] = $field_id;
 
       // Check for reserved and other illegal field IDs.
-      if (Utility::isFieldIdReserved($new_id)) {
+      if ($this->fieldsHelper->isFieldIdReserved($new_id)) {
         $args = array(
           '%field_id' => $new_id,
         );
@@ -403,7 +417,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   public function save(array $form, FormStateInterface $form_state) {
     $index = $this->entity;
     if ($index instanceof UnsavedConfigurationInterface) {
-      $index->savePermanent($this->getEntityTypeManager());
+      $index->savePermanent();
     }
     else {
       $index->save();
diff --git a/src/Form/IndexForm.php b/src/Form/IndexForm.php
index 352ffcc6..f322f0c1 100644
--- a/src/Form/IndexForm.php
+++ b/src/Form/IndexForm.php
@@ -9,7 +9,6 @@
 use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\Core\Form\SubformState;
 use Drupal\Core\Utility\Error;
-use Drupal\search_api\Datasource\DatasourcePluginManager;
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\SearchApiException;
 use Drupal\search_api\Tracker\TrackerPluginManager;
@@ -28,13 +27,6 @@ class IndexForm extends EntityForm {
   protected $entityTypeManager;
 
   /**
-   * The datasource plugin manager.
-   *
-   * @var \Drupal\search_api\Datasource\DatasourcePluginManager
-   */
-  protected $datasourcePluginManager;
-
-  /**
    * The tracker plugin manager.
    *
    * @var \Drupal\search_api\Tracker\TrackerPluginManager
@@ -53,14 +45,11 @@ class IndexForm extends EntityForm {
    *
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
-   * @param \Drupal\search_api\Datasource\DatasourcePluginManager $datasource_plugin_manager
-   *   The search datasource plugin manager.
    * @param \Drupal\search_api\Tracker\TrackerPluginManager $tracker_plugin_manager
    *   The Search API tracker plugin manager.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, DatasourcePluginManager $datasource_plugin_manager, TrackerPluginManager $tracker_plugin_manager) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, TrackerPluginManager $tracker_plugin_manager) {
     $this->entityTypeManager = $entity_type_manager;
-    $this->datasourcePluginManager = $datasource_plugin_manager;
     $this->trackerPluginManager = $tracker_plugin_manager;
   }
 
@@ -68,63 +57,9 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Dat
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
-    /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
     $entity_type_manager = $container->get('entity_type.manager');
-    /** @var \Drupal\search_api\Datasource\DatasourcePluginManager $datasource_plugin_manager */
-    $datasource_plugin_manager = $container->get('plugin.manager.search_api.datasource');
-    /** @var \Drupal\search_api\Tracker\TrackerPluginManager $tracker_plugin_manager */
     $tracker_plugin_manager = $container->get('plugin.manager.search_api.tracker');
-    return new static($entity_type_manager, $datasource_plugin_manager, $tracker_plugin_manager);
-  }
-
-  /**
-   * Returns the entity type manager.
-   *
-   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
-   *   The entity type manager.
-   */
-  protected function getEntityTypeManager() {
-    return $this->entityTypeManager ?: \Drupal::service('entity_type.manager');
-  }
-
-  /**
-   * Returns the datasource plugin manager.
-   *
-   * @return \Drupal\search_api\Datasource\DatasourcePluginManager
-   *   The datasource plugin manager.
-   */
-  protected function getDatasourcePluginManager() {
-    return $this->datasourcePluginManager ?: \Drupal::service('plugin.manager.search_api.datasource');
-  }
-
-  /**
-   * Returns the tracker plugin manager.
-   *
-   * @return \Drupal\search_api\Tracker\TrackerPluginManager
-   *   The tracker plugin manager.
-   */
-  protected function getTrackerPluginManager() {
-    return $this->trackerPluginManager ?: \Drupal::service('plugin.manager.search_api.tracker');
-  }
-
-  /**
-   * Returns the index storage controller.
-   *
-   * @return \Drupal\Core\Entity\EntityStorageInterface
-   *   The index storage controller.
-   */
-  protected function getIndexStorage() {
-    return $this->getEntityTypeManager()->getStorage('search_api_index');
-  }
-
-  /**
-   * Returns the server storage controller.
-   *
-   * @return \Drupal\Core\Entity\EntityStorageInterface
-   *   The server storage controller.
-   */
-  protected function getServerStorage() {
-    return $this->getEntityTypeManager()->getStorage('search_api_server');
+    return new static($entity_type_manager, $tracker_plugin_manager);
   }
 
   /**
@@ -135,8 +70,11 @@ protected function getServerStorage() {
    */
   protected function getServerOptions() {
     $options = array();
-    /** @var \Drupal\search_api\ServerInterface $server */
-    foreach ($this->getServerStorage()->loadMultiple() as $server_id => $server) {
+    /** @var \Drupal\search_api\ServerInterface[] $servers */
+    $servers = $this->entityTypeManager
+      ->getStorage('search_api_server')
+      ->loadMultiple();
+    foreach ($servers as $server_id => $server) {
       // @todo Special formatting for disabled servers.
       $options[$server_id] = Html::escape($server->label());
     }
@@ -191,13 +129,14 @@ public function buildEntityForm(array &$form, FormStateInterface $form_state, In
       '#default_value' => $index->label(),
       '#required' => TRUE,
     );
+    $index_storage = $this->entityTypeManager->getStorage('search_api_index');
     $form['id'] = array(
       '#type' => 'machine_name',
       '#default_value' => $index->isNew() ? NULL : $index->id(),
       '#maxlength' => 50,
       '#required' => TRUE,
       '#machine_name' => array(
-        'exists' => array($this->getIndexStorage(), 'load'),
+        'exists' => array($index_storage, 'load'),
         'source' => array('name'),
       ),
       '#disabled' => !$index->isNew(),
@@ -431,7 +370,7 @@ public function buildTrackerConfigForm(array &$form, FormStateInterface $form_st
     else {
       // Probably an AJAX rebuild of the form – use the tracker selected by
       // the user.
-      $tracker = $this->getTrackerPluginManager()->createInstance($selected_tracker, array());
+      $tracker = $this->trackerPluginManager->createInstance($selected_tracker, array());
     }
 
     if (empty($tracker)) {
diff --git a/src/Form/IndexStatusForm.php b/src/Form/IndexStatusForm.php
index 33c0db67..481abdca 100644
--- a/src/Form/IndexStatusForm.php
+++ b/src/Form/IndexStatusForm.php
@@ -8,6 +8,7 @@
 use Drupal\search_api\IndexBatchHelper;
 use Drupal\search_api\SearchApiException;
 use Drupal\search_api\IndexInterface;
+use Drupal\search_api\Task\IndexTaskManagerInterface;
 
 /**
  * Provides a form for indexing, clearing, etc., an index.
@@ -15,6 +16,36 @@
 class IndexStatusForm extends FormBase {
 
   /**
+   * The index task manager.
+   *
+   * @var \Drupal\search_api\Task\IndexTaskManagerInterface|null
+   */
+  protected $indexTaskManager;
+
+  /**
+   * Retrieves the index task manager.
+   *
+   * @return \Drupal\search_api\Task\IndexTaskManagerInterface
+   *   The index task manager.
+   */
+  public function getIndexTaskManager() {
+    return $this->indexTaskManager ?: \Drupal::service('search_api.index_task_manager');
+  }
+
+  /**
+   * Sets the index task manager.
+   *
+   * @param \Drupal\search_api\Task\IndexTaskManagerInterface $index_task_manager
+   *   The new index task manager.
+   *
+   * @return $this
+   */
+  public function setIndexTaskManager(IndexTaskManagerInterface $index_task_manager) {
+    $this->indexTaskManager = $index_task_manager;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
@@ -34,7 +65,7 @@ public function buildForm(array $form, FormStateInterface $form_state, IndexInte
     $form['#attached']['library'][] = 'search_api/drupal.search_api.admin_css';
 
     if ($index->hasValidTracker()) {
-      if (!\Drupal::getContainer()->get('search_api.index_task_manager')->isTrackingComplete($index)) {
+      if (!$this->getIndexTaskManager()->isTrackingComplete($index)) {
         $form['tracking'] = array(
           '#type' => 'details',
           '#title' => $this->t('Track items for index'),
@@ -196,7 +227,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
         break;
 
       case 'track_now':
-        \Drupal::getContainer()->get('search_api.index_task_manager')->addItemsBatch($index);
+        $this->getIndexTaskManager()->addItemsBatch($index);
         break;
     }
   }
diff --git a/src/Form/ServerForm.php b/src/Form/ServerForm.php
index 26e63eef..28364444 100644
--- a/src/Form/ServerForm.php
+++ b/src/Form/ServerForm.php
@@ -58,26 +58,6 @@ public static function create(ContainerInterface $container) {
   }
 
   /**
-   * Retrieves the server storage controller.
-   *
-   * @return \Drupal\Core\Entity\EntityStorageInterface
-   *   The server storage controller.
-   */
-  protected function getStorage() {
-    return $this->storage ?: \Drupal::service('entity_type.manager')->getStorage('search_api_server');
-  }
-
-  /**
-   * Retrieves the backend plugin manager.
-   *
-   * @return \Drupal\search_api\Backend\BackendPluginManager
-   *   The backend plugin manager.
-   */
-  protected function getBackendPluginManager() {
-    return $this->backendPluginManager ?: \Drupal::service('plugin.manager.search_api.backend');
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
@@ -133,7 +113,7 @@ public function buildEntityForm(array &$form, FormStateInterface $form_state, Se
       '#maxlength' => 50,
       '#required' => TRUE,
       '#machine_name' => array(
-        'exists' => array($this->getStorage(), 'load'),
+        'exists' => array($this->storage, 'load'),
         'source' => array('name'),
       ),
       '#disabled' => !$server->isNew(),
@@ -151,13 +131,13 @@ public function buildEntityForm(array &$form, FormStateInterface $form_state, Se
       '#default_value' => $server->getDescription(),
     );
 
-    $backends = $this->getBackendPluginManager()->getDefinitions();
+    $backends = $this->backendPluginManager->getDefinitions();
     $backend_options = [];
     $descriptions = [];
     foreach ($backends as $backend_id => $definition) {
       $config = $backend_id === $server->getBackendId() ? $server->getBackendConfig() : [];
       $config['#server'] = $server;
-      $backend = $this->getBackendPluginManager()
+      $backend = $this->backendPluginManager
         ->createInstance($backend_id, $config);
       $backend_options[$backend_id] = $backend->label();
       $descriptions[$backend_id]['#description'] = $backend->getDescription();
diff --git a/src/Form/UnsavedConfigurationFormTrait.php b/src/Form/UnsavedConfigurationFormTrait.php
index 15b4e57e..d4be3ffa 100644
--- a/src/Form/UnsavedConfigurationFormTrait.php
+++ b/src/Form/UnsavedConfigurationFormTrait.php
@@ -24,16 +24,6 @@
   protected $dateFormatter;
 
   /**
-   * Retrieves the entity type manager.
-   *
-   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
-   *   The entity type manager.
-   */
-  protected function getEntityTypeManager() {
-    return isset($this->entityTypeManager) ? $this->entityTypeManager : \Drupal::entityTypeManager();
-  }
-
-  /**
    * Retrieves the renderer.
    *
    * @return \Drupal\Core\Render\RendererInterface
@@ -76,7 +66,7 @@ protected function checkEntityEditable(array &$form, $entity, $reportChanged = F
         $form['#disabled'] = TRUE;
         $username = array(
           '#theme' => 'username',
-          '#account' => $entity->getLockOwner($this->getEntityTypeManager()),
+          '#account' => $entity->getLockOwner(),
         );
         $lockMessageSubstitutions = array(
           '@user' => $this->renderer->render($username),
diff --git a/src/IndexInterface.php b/src/IndexInterface.php
index 32e02fcc..c8f8813b 100644
--- a/src/IndexInterface.php
+++ b/src/IndexInterface.php
@@ -68,20 +68,6 @@ public function getOption($name, $default = NULL);
    * - cron_limit: The maximum number of items to be indexed per cron batch.
    * - index_directly: Boolean setting whether entities are indexed immediately
    *   after they are created or updated.
-   * - fields: An array of all indexed fields for this index. Keys are the field
-   *   identifiers, the values are arrays for specifying the field settings. The
-   *   structure of those arrays looks like this:
-   *   - type: The type set for this field. One of the types returned by
-   *     \Drupal\search_api\Utility\Utility::getDefaultDataTypes().
-   *   - boost: (optional) A boost value for terms found in this field during
-   *     searches. Usually only relevant for fulltext fields. Defaults to 1.0.
-   * - processors: An array of all processors available for the index. The keys
-   *   are the processor identifiers, the values are arrays containing the
-   *   settings for that processor. The inner structure looks like this:
-   *   - status: Boolean indicating whether the processor is enabled.
-   *   - weight: Used for sorting the processors.
-   *   - settings: Processor-specific settings, configured via the processor's
-   *     configuration form.
    *
    * @return array
    *   An associative array of option values, keyed by the option name.
diff --git a/src/ParamConverter/SearchApiConverter.php b/src/ParamConverter/SearchApiConverter.php
index 510d2271..616fad0c 100644
--- a/src/ParamConverter/SearchApiConverter.php
+++ b/src/ParamConverter/SearchApiConverter.php
@@ -65,10 +65,11 @@ public function convert($value, $definition, $name, array $defaults) {
     // store the lock metadata.
     $store = $this->tempStoreFactory->get('search_api_index');
     $current_user_id = $this->currentUser->id() ?: session_id();
-    /** @var \Drupal\search_api\IndexInterface|\Drupal\search_api\UnsavedConfigurationInterface $index */
+    /** @var \Drupal\search_api\IndexInterface|\Drupal\search_api\UnsavedIndexConfiguration $index */
     if ($index = $store->get($value)) {
       $index = new UnsavedIndexConfiguration($index, $store, $current_user_id);
       $index->setLockInformation($store->getMetadata($value));
+      $index->setEntityTypeManager($this->entityManager);
     }
     // Otherwise, create a new temporary copy of the search index.
     else {
diff --git a/src/Plugin/search_api/datasource/ContentEntity.php b/src/Plugin/search_api/datasource/ContentEntity.php
index 49b35eaf..0059b409 100644
--- a/src/Plugin/search_api/datasource/ContentEntity.php
+++ b/src/Plugin/search_api/datasource/ContentEntity.php
@@ -91,6 +91,13 @@ class ContentEntity extends DatasourcePluginBase implements EntityDatasourceInte
   protected $languageManager;
 
   /**
+   * The fields helper.
+   *
+   * @var \Drupal\search_api\Utility\FieldsHelperInterface|null
+   */
+  protected $fieldsHelper;
+
+  /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
@@ -121,6 +128,7 @@ public static function create(ContainerInterface $container, array $configuratio
     $datasource->setTypedDataManager($container->get('typed_data_manager'));
     $datasource->setConfigFactory($container->get('config.factory'));
     $datasource->setLanguageManager($container->get('language_manager'));
+    $datasource->setFieldsHelper($container->get('search_api.fields_helper'));
 
     return $datasource;
   }
@@ -136,43 +144,13 @@ public function getEntityTypeManager() {
   }
 
   /**
-   * Retrieves the entity field manager.
-   *
-   * @return \Drupal\Core\Entity\EntityFieldManagerInterface
-   *   The entity field manager.
-   */
-  public function getEntityFieldManager() {
-    return $this->entityFieldManager ?: \Drupal::getContainer()->get('entity_field.manager');
-  }
-
-  /**
-   * Retrieves the entity display repository.
-   *
-   * @return \Drupal\Core\Entity\EntityDisplayRepositoryInterface
-   *   The entity entity display repository.
-   */
-  public function getEntityDisplayRepository() {
-    return $this->entityDisplayRepository ?: \Drupal::getContainer()->get('entity_display.repository');
-  }
-
-  /**
-   * Retrieves the entity display repository.
-   *
-   * @return \Drupal\Core\Entity\EntityTypeBundleInfoInterface
-   *   The entity entity display repository.
-   */
-  public function getEntityTypeBundleInfo() {
-    return $this->entityTypeBundleInfo ?: \Drupal::getContainer()->get('entity_type.bundle.info');
-  }
-
-  /**
    * Retrieves the entity storage.
    *
    * @return \Drupal\Core\Entity\EntityStorageInterface
    *   The entity storage.
    */
   protected function getEntityStorage() {
-    return $this->getEntityTypeManager()->getStorage($this->pluginDefinition['entity_type']);
+    return $this->getEntityTypeManager()->getStorage($this->getEntityTypeId());
   }
 
   /**
@@ -182,7 +160,8 @@ protected function getEntityStorage() {
    *   The entity type definition.
    */
   protected function getEntityType() {
-    return $this->getEntityTypeManager()->getDefinition($this->getEntityTypeId());
+    return $this->getEntityTypeManager()
+      ->getDefinition($this->getEntityTypeId());
   }
 
   /**
@@ -199,6 +178,16 @@ public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_man
   }
 
   /**
+   * Retrieves the entity field manager.
+   *
+   * @return \Drupal\Core\Entity\EntityFieldManagerInterface
+   *   The entity field manager.
+   */
+  public function getEntityFieldManager() {
+    return $this->entityFieldManager ?: \Drupal::service('entity_field.manager');
+  }
+
+  /**
    * Sets the entity field manager.
    *
    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
@@ -212,6 +201,16 @@ public function setEntityFieldManager(EntityFieldManagerInterface $entity_field_
   }
 
   /**
+   * Retrieves the entity display repository.
+   *
+   * @return \Drupal\Core\Entity\EntityDisplayRepositoryInterface
+   *   The entity entity display repository.
+   */
+  public function getEntityDisplayRepository() {
+    return $this->entityDisplayRepository ?: \Drupal::service('entity_display.repository');
+  }
+
+  /**
    * Sets the entity display repository.
    *
    * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
@@ -225,6 +224,16 @@ public function setEntityDisplayRepository(EntityDisplayRepositoryInterface $ent
   }
 
   /**
+   * Retrieves the entity display repository.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeBundleInfoInterface
+   *   The entity entity display repository.
+   */
+  public function getEntityTypeBundleInfo() {
+    return $this->entityTypeBundleInfo ?: \Drupal::service('entity_type.bundle.info');
+  }
+
+  /**
    * Sets the entity type bundle info.
    *
    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
@@ -317,6 +326,29 @@ public function setLanguageManager(LanguageManagerInterface $language_manager) {
   }
 
   /**
+   * Retrieves the fields helper.
+   *
+   * @return \Drupal\search_api\Utility\FieldsHelperInterface
+   *   The fields helper.
+   */
+  public function getFieldsHelper() {
+    return $this->fieldsHelper ?: \Drupal::service('search_api.fields_helper');
+  }
+
+  /**
+   * Sets the fields helper.
+   *
+   * @param \Drupal\search_api\Utility\FieldsHelperInterface $fields_helper
+   *   The new fields helper.
+   *
+   * @return $this
+   */
+  public function setFieldsHelper(FieldsHelperInterface $fields_helper) {
+    $this->fieldsHelper = $fields_helper;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getPropertyDefinitions() {
@@ -633,7 +665,9 @@ protected function getEntityBundles() {
    * {@inheritdoc}
    */
   public function getPartialItemIds($page = NULL, array $bundles = NULL, array $languages = NULL) {
-    $select = \Drupal::entityQuery($this->getEntityTypeId());
+    $select = $this->getEntityTypeManager()
+      ->getStorage($this->getEntityTypeId())
+      ->getQuery();
 
     // We want to determine all entities of either one of the given bundles OR
     // one of the given languages. That means we can't just filter for $bundles
@@ -878,7 +912,7 @@ protected function getPropertyPathDependencies($property_path, array $properties
       }
     }
 
-    $property = Utility::getInnerProperty($property);
+    $property = $this->getFieldsHelper()->getInnerProperty($property);
 
     if ($property instanceof EntityDataDefinitionInterface) {
       $entity_type_definition = $this->getEntityTypeManager()
@@ -890,7 +924,8 @@ protected function getPropertyPathDependencies($property_path, array $properties
     }
 
     if (isset($nested_path) && $property instanceof ComplexDataDefinitionInterface) {
-      $nested_dependencies = $this->getPropertyPathDependencies($nested_path, Utility::getNestedProperties($property));
+      $nested = $this->getFieldsHelper()->getNestedProperties($property);
+      $nested_dependencies = $this->getPropertyPathDependencies($nested_path, $nested);
       foreach ($nested_dependencies as $type => $names) {
         $dependencies += array($type => array());
         $dependencies[$type] += $names;
diff --git a/src/Plugin/search_api/processor/ContentAccess.php b/src/Plugin/search_api/processor/ContentAccess.php
index 8c68d529..30e7b84b 100644
--- a/src/Plugin/search_api/processor/ContentAccess.php
+++ b/src/Plugin/search_api/processor/ContentAccess.php
@@ -5,6 +5,7 @@
 use Drupal\comment\CommentInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Session\AccountProxyInterface;
 use Drupal\Core\Session\AnonymousUserSession;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\node\NodeInterface;
@@ -45,6 +46,13 @@ class ContentAccess extends ProcessorPluginBase {
   protected $database;
 
   /**
+   * The current_user service used by this plugin.
+   *
+   * @var \Drupal\Core\Session\AccountProxyInterface|null
+   */
+  protected $currentUser;
+
+  /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
@@ -53,6 +61,7 @@ public static function create(ContainerInterface $container, array $configuratio
 
     $processor->setLogger($container->get('logger.channel.search_api'));
     $processor->setDatabase($container->get('database'));
+    $processor->setCurrentUser($container->get('current_user'));
 
     return $processor;
   }
@@ -81,6 +90,29 @@ public function setDatabase(Connection $database) {
   }
 
   /**
+   * Retrieves the current user.
+   *
+   * @return \Drupal\Core\Session\AccountProxyInterface
+   *   The current user.
+   */
+  public function getCurrentUser() {
+    return $this->currentUser ?: \Drupal::currentUser();
+  }
+
+  /**
+   * Sets the current user.
+   *
+   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
+   *   The current user.
+   *
+   * @return $this
+   */
+  public function setCurrentUser(AccountProxyInterface $current_user) {
+    $this->currentUser = $current_user;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public static function supportsIndex(IndexInterface $index) {
@@ -204,7 +236,7 @@ protected function getNode(ComplexDataInterface $item) {
    */
   public function preprocessSearchQuery(QueryInterface $query) {
     if (!$query->getOption('search_api_bypass_access')) {
-      $account = $query->getOption('search_api_access_account', \Drupal::currentUser());
+      $account = $query->getOption('search_api_access_account', $this->getCurrentUser());
       if (is_numeric($account)) {
         $account = User::load($account);
       }
@@ -217,7 +249,7 @@ public function preprocessSearchQuery(QueryInterface $query) {
         }
       }
       else {
-        $account = $query->getOption('search_api_access_account', \Drupal::currentUser());
+        $account = $query->getOption('search_api_access_account', $this->getCurrentUser());
         if ($account instanceof AccountInterface) {
           $account = $account->id();
         }
diff --git a/src/Plugin/search_api/processor/Highlight.php b/src/Plugin/search_api/processor/Highlight.php
index feebcc7a..40354a0f 100644
--- a/src/Plugin/search_api/processor/Highlight.php
+++ b/src/Plugin/search_api/processor/Highlight.php
@@ -11,6 +11,7 @@
 use Drupal\search_api\Processor\ProcessorPluginBase;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\Utility\DataTypeHelperInterface;
 use Drupal\search_api\Utility\Utility;
 
 /**
@@ -51,6 +52,13 @@ class Highlight extends ProcessorPluginBase implements PluginFormInterface {
   protected static $split;
 
   /**
+   * The data type helper.
+   *
+   * @var \Drupal\search_api\Utility\DataTypeHelperInterface|null
+   */
+  protected $dataTypeHelper;
+
+  /**
    * {@inheritdoc}
    */
   public function __construct(array $configuration, $plugin_id, array $plugin_definition) {
@@ -68,6 +76,29 @@ public function __construct(array $configuration, $plugin_id, array $plugin_defi
   }
 
   /**
+   * Retrieves the data type helper.
+   *
+   * @return \Drupal\search_api\Utility\DataTypeHelperInterface
+   *   The data type helper.
+   */
+  public function getDataTypeHelper() {
+    return $this->dataTypeHelper ?: \Drupal::service('search_api.data_type_helper');
+  }
+
+  /**
+   * Sets the data type helper.
+   *
+   * @param \Drupal\search_api\Utility\DataTypeHelperInterface $data_type_helper
+   *   The new data type helper.
+   *
+   * @return $this
+   */
+  public function setDataTypeHelper(DataTypeHelperInterface $data_type_helper) {
+    $this->dataTypeHelper = $data_type_helper;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function preIndexSave() {
@@ -278,7 +309,6 @@ protected function highlightFields(array $results, array $keys) {
     $item_fields = $this->getFulltextFields($results, NULL, $this->configuration['highlight'] == 'always');
     $highlighted_fields = array();
     foreach ($item_fields as $item_id => $fields) {
-      /** @var \Drupal\search_api\Item\FieldInterface $field */
       foreach ($fields as $field_id => $values) {
         $change = FALSE;
         foreach ($values as $i => $value) {
@@ -308,9 +338,9 @@ protected function highlightFields(array $results, array $keys) {
    *   (optional) If FALSE, only field values already present will be returned.
    *   Otherwise, fields will be loaded if necessary.
    *
-   * @return \Drupal\search_api\Item\FieldInterface[][]
-   *   An two-dimensional array of fulltext fields, keyed first by item ID and
-   *   then field ID.
+   * @return mixed[][][]
+   *   Field values extracted from the result items' fulltext fields, keyed by
+   *   item ID, field ID and then numeric indices.
    */
   protected function getFulltextFields(array $result_items, array $fulltext_fields = NULL, $load = TRUE) {
     // All the index's fulltext fields, grouped by datasource.
@@ -319,7 +349,7 @@ protected function getFulltextFields(array $result_items, array $fulltext_fields
       if (isset($fulltext_fields) && !in_array($field_id, $fulltext_fields)) {
         continue;
       }
-      if (Utility::isTextType($field->getType())) {
+      if ($this->getDataTypeHelper()->isTextType($field->getType())) {
         $fields_by_datasource[$field->getDatasourceId()][$field->getPropertyPath()] = $field_id;
       }
     }
diff --git a/src/Plugin/search_api/processor/HtmlFilter.php b/src/Plugin/search_api/processor/HtmlFilter.php
index 4db3ff06..7762e7dd 100644
--- a/src/Plugin/search_api/processor/HtmlFilter.php
+++ b/src/Plugin/search_api/processor/HtmlFilter.php
@@ -9,6 +9,7 @@
 use Drupal\search_api\Item\FieldInterface;
 use Drupal\search_api\Plugin\search_api\data_type\value\TextValueInterface;
 use Drupal\search_api\Processor\FieldsProcessorPluginBase;
+use Drupal\search_api\Utility\DataTypeHelperInterface;
 use Drupal\search_api\Utility\Utility;
 use Symfony\Component\Yaml\Dumper;
 use Symfony\Component\Yaml\Exception\ParseException;
@@ -31,6 +32,36 @@
 class HtmlFilter extends FieldsProcessorPluginBase {
 
   /**
+   * The data type helper.
+   *
+   * @var \Drupal\search_api\Utility\DataTypeHelperInterface|null
+   */
+  protected $dataTypeHelper;
+
+  /**
+   * Retrieves the data type helper.
+   *
+   * @return \Drupal\search_api\Utility\DataTypeHelperInterface
+   *   The data type helper.
+   */
+  public function getDataTypeHelper() {
+    return $this->dataTypeHelper ?: \Drupal::service('search_api.data_type_helper');
+  }
+
+  /**
+   * Sets the data type helper.
+   *
+   * @param \Drupal\search_api\Utility\DataTypeHelperInterface $data_type_helper
+   *   The new data type helper.
+   *
+   * @return $this
+   */
+  public function setDataTypeHelper(DataTypeHelperInterface $data_type_helper) {
+    $this->dataTypeHelper = $data_type_helper;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function defaultConfiguration() {
@@ -161,7 +192,7 @@ protected function processFieldValue(&$value, $type) {
     // Remove invisible content.
     $text = preg_replace('@<(applet|audio|canvas|command|embed|iframe|map|menu|noembed|noframes|noscript|script|style|svg|video)[^>]*>.*</\1>@siU', ' ', $value);
     // Let removed tags still delimit words.
-    $is_text_type = Utility::isTextType($type);
+    $is_text_type = $this->getDataTypeHelper()->isTextType($type);
     if ($is_text_type) {
       $text = str_replace(array('<', '>'), array(' <', '> '), $text);
       if ($this->configuration['title']) {
diff --git a/src/Plugin/search_api/processor/Property/RenderedItemProperty.php b/src/Plugin/search_api/processor/Property/RenderedItemProperty.php
index 88cb8c39..b67bb315 100644
--- a/src/Plugin/search_api/processor/Property/RenderedItemProperty.php
+++ b/src/Plugin/search_api/processor/Property/RenderedItemProperty.php
@@ -10,6 +10,8 @@
 
 /**
  * Defines a "rendered item" property.
+ *
+ * @see \Drupal\search_api\Plugin\search_api\processor\RenderedItem
  */
 class RenderedItemProperty extends ConfigurablePropertyBase {
 
diff --git a/src/Plugin/search_api/processor/RenderedItem.php b/src/Plugin/search_api/processor/RenderedItem.php
index 1dd7e83e..375d9aa1 100644
--- a/src/Plugin/search_api/processor/RenderedItem.php
+++ b/src/Plugin/search_api/processor/RenderedItem.php
@@ -19,6 +19,8 @@
 /**
  * Adds an additional field containing the rendered item.
  *
+ * @see \Drupal\search_api\Plugin\search_api\processor\Property\RenderedItemProperty
+ *
  * @SearchApiProcessor(
  *   id = "rendered_item",
  *   label = @Translation("Rendered item"),
diff --git a/src/Plugin/search_api/processor/Transliteration.php b/src/Plugin/search_api/processor/Transliteration.php
index a276651d..95b9cb6b 100644
--- a/src/Plugin/search_api/processor/Transliteration.php
+++ b/src/Plugin/search_api/processor/Transliteration.php
@@ -3,6 +3,7 @@
 namespace Drupal\search_api\Plugin\search_api\processor;
 
 use Drupal\Component\Transliteration\TransliterationInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\search_api\Processor\FieldsProcessorPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -30,6 +31,13 @@ class Transliteration extends FieldsProcessorPluginBase {
   protected $transliterator;
 
   /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface|null
+   */
+  protected $languageManager;
+
+  /**
    * The language to use for transliterating.
    *
    * @var string
@@ -43,12 +51,10 @@ public static function create(ContainerInterface $container, array $configuratio
     /** @var static $processor */
     $processor = parent::create($container, $configuration, $plugin_id, $plugin_definition);
 
-    /** @var \Drupal\Component\Transliteration\TransliterationInterface $transliterator */
-    $transliterator = $container->get('transliteration');
-    $processor->setTransliterator($transliterator);
-    /** @var \Drupal\Core\Language\LanguageManagerInterface $language_manager */
-    $language_manager = $container->get('language_manager');
-    $processor->setLangcode($language_manager->getDefaultLanguage()->getId());
+    $processor->setTransliterator($container->get('transliteration'));
+    $processor->setLanguageManager($container->get('language_manager'));
+    $langcode = $processor->getLanguageManager()->getDefaultLanguage()->getId();
+    $processor->setLangcode($langcode);
 
     return $processor;
   }
@@ -77,13 +83,38 @@ public function setTransliterator(TransliterationInterface $transliterator) {
   }
 
   /**
+   * Retrieves the language manager.
+   *
+   * @return \Drupal\Core\Language\LanguageManagerInterface
+   *   The language manager.
+   */
+  public function getLanguageManager() {
+    return $this->languageManager ?: \Drupal::languageManager();
+  }
+
+  /**
+   * Sets the language manager.
+   *
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The new language manager.
+   *
+   * @return $this
+   */
+  public function setLanguageManager(LanguageManagerInterface $language_manager) {
+    $this->languageManager = $language_manager;
+    return $this;
+  }
+
+  /**
    * Retrieves the langcode.
    *
    * @return string
    *   The langcode.
    */
   public function getLangcode() {
-    return $this->langcode ?: \Drupal::languageManager()->getDefaultLanguage()->getId();
+    return $this->langcode ?: $this->getLanguageManager()
+      ->getDefaultLanguage()
+      ->getId();
   }
 
   /**
diff --git a/src/Plugin/views/cache/SearchApiCachePluginTrait.php b/src/Plugin/views/cache/SearchApiCachePluginTrait.php
index a2e22fdb..71751b67 100644
--- a/src/Plugin/views/cache/SearchApiCachePluginTrait.php
+++ b/src/Plugin/views/cache/SearchApiCachePluginTrait.php
@@ -4,8 +4,11 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\Context\CacheContextsManager;
 use Drupal\search_api\Plugin\views\query\SearchApiQuery;
 use Drupal\search_api\SearchApiException;
+use Drupal\search_api\Utility\QueryHelperInterface;
 
 /**
  * Provides a trait to use in Views cache plugins for Search API queries.
@@ -13,6 +16,96 @@
 trait SearchApiCachePluginTrait {
 
   /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|null
+   */
+  protected $cacheBackend;
+
+  /**
+   * The cache contexts manager.
+   *
+   * @var \Drupal\Core\Cache\Context\CacheContextsManager|null
+   */
+  protected $cacheContextsManager;
+
+  /**
+   * The query helper.
+   *
+   * @var \Drupal\search_api\Utility\QueryHelperInterface|null
+   */
+  protected $queryHelper;
+
+  /**
+   * Retrieves the cache backend.
+   *
+   * @return \Drupal\Core\Cache\CacheBackendInterface
+   *   The cache backend.
+   */
+  public function getCacheBackend() {
+    return $this->cacheBackend ?: \Drupal::cache($this->resultsBin);
+  }
+
+  /**
+   * Sets the cache backend.
+   *
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   The new cache backend.
+   *
+   * @return $this
+   */
+  public function setCacheBackend(CacheBackendInterface $cache_backend) {
+    $this->cacheBackend = $cache_backend;
+    return $this;
+  }
+
+  /**
+   * Retrieves the cache contexts manager.
+   *
+   * @return \Drupal\Core\Cache\Context\CacheContextsManager
+   *   The cache contexts manager.
+   */
+  public function getCacheContextsManager() {
+    return $this->cacheContextsManager ?: \Drupal::service('cache_contexts_manager');
+  }
+
+  /**
+   * Sets the cache contexts manager.
+   *
+   * @param \Drupal\Core\Cache\Context\CacheContextsManager $cache_contexts_manager
+   *   The new cache contexts manager.
+   *
+   * @return $this
+   */
+  public function setCacheContextsManager(CacheContextsManager $cache_contexts_manager) {
+    $this->cacheContextsManager = $cache_contexts_manager;
+    return $this;
+  }
+
+  /**
+   * Retrieves the query helper.
+   *
+   * @return \Drupal\search_api\Utility\QueryHelperInterface
+   *   The query helper.
+   */
+  public function getQueryHelper() {
+    return $this->queryHelper ?: \Drupal::service('search_api.query_helper');
+  }
+
+  /**
+   * Sets the query helper.
+   *
+   * @param \Drupal\search_api\Utility\QueryHelperInterface $query_helper
+   *   The new query helper.
+   *
+   * @return $this
+   */
+  public function setQueryHelper(QueryHelperInterface $query_helper) {
+    $this->queryHelper = $query_helper;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function cacheSet($type) {
@@ -32,7 +125,7 @@ public function cacheSet($type) {
     if ($expire !== Cache::PERMANENT) {
       $expire += (int) $this->view->getRequest()->server->get('REQUEST_TIME');
     }
-    \Drupal::cache($this->resultsBin)
+    $this->getCacheBackend()
       ->set($this->generateResultsKey(), $data, $expire, $this->getCacheTags());
   }
 
@@ -46,7 +139,7 @@ public function cacheGet($type) {
 
     // Values to set: $view->result, $view->total_rows, $view->execute_time,
     // $view->current_page.
-    if ($cache = \Drupal::cache($this->resultsBin)->get($this->generateResultsKey())) {
+    if ($cache = $this->getCacheBackend()->get($this->generateResultsKey())) {
       $cutoff = $this->cacheExpire($type);
       if (!$cutoff || $cache->created > $cutoff) {
         $this->view->result = $cache->data['result'];
@@ -58,9 +151,7 @@ public function cacheGet($type) {
         // et al. work.
         /** @var \Drupal\search_api\Query\ResultSetInterface $results */
         $results = $cache->data['search_api results'];
-        \Drupal::getContainer()
-          ->get('search_api.query_helper')
-          ->addResults($results);
+        $this->getQueryHelper()->addResults($results);
 
         try {
           $this->getQuery()->setSearchApiResults($results);
@@ -91,7 +182,7 @@ public function generateResultsKey() {
       $display_handler_cache_contexts = $this->displayHandler
         ->getCacheMetadata()
         ->getCacheContexts();
-      $key_data += \Drupal::service('cache_contexts_manager')
+      $key_data += $this->getCacheContextsManager()
         ->convertTokensToKeys($display_handler_cache_contexts)
         ->getKeys();
 
diff --git a/src/Plugin/views/field/SearchApiFieldTrait.php b/src/Plugin/views/field/SearchApiFieldTrait.php
index 823d6b59..7c0e3b4e 100644
--- a/src/Plugin/views/field/SearchApiFieldTrait.php
+++ b/src/Plugin/views/field/SearchApiFieldTrait.php
@@ -8,8 +8,10 @@
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\DataReferenceInterface;
 use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TypedDataManagerInterface;
 use Drupal\search_api\Plugin\views\SearchApiHandlerTrait;
 use Drupal\search_api\Processor\ProcessorPropertyInterface;
+use Drupal\search_api\Utility\FieldsHelperInterface;
 use Drupal\search_api\Utility\Utility;
 use Drupal\views\Plugin\views\field\MultiItemsFieldHandlerInterface;
 use Drupal\views\ResultRow;
@@ -88,6 +90,66 @@
   protected $skipAccessChecks = array();
 
   /**
+   * The fields helper.
+   *
+   * @var \Drupal\search_api\Utility\FieldsHelperInterface|null
+   */
+  protected $fieldsHelper;
+
+  /**
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManagerInterface|null
+   */
+  protected $typedDataManager;
+
+  /**
+   * Retrieves the typed data manager.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataManagerInterface
+   *   The typed data manager.
+   */
+  public function getTypedDataManager() {
+    return $this->typedDataManager ?: \Drupal::service('typed_data_manager');
+  }
+
+  /**
+   * Sets the typed data manager.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataManagerInterface $typed_data_manager
+   *   The new typed data manager.
+   *
+   * @return $this
+   */
+  public function setTypedDataManager(TypedDataManagerInterface $typed_data_manager) {
+    $this->typedDataManager = $typed_data_manager;
+    return $this;
+  }
+
+  /**
+   * Retrieves the fields helper.
+   *
+   * @return \Drupal\search_api\Utility\FieldsHelperInterface
+   *   The fields helper.
+   */
+  public function getFieldsHelper() {
+    return $this->fieldsHelper ?: \Drupal::service('search_api.fields_helper');
+  }
+
+  /**
+   * Sets the fields helper.
+   *
+   * @param \Drupal\search_api\Utility\FieldsHelperInterface $fields_helper
+   *   The new fields helper.
+   *
+   * @return $this
+   */
+  public function setFieldsHelper(FieldsHelperInterface $fields_helper) {
+    $this->fieldsHelper = $fields_helper;
+    return $this;
+  }
+
+  /**
    * Determines whether this field can have multiple values.
    *
    * When this can't be reliably determined, the method defaults to TRUE.
@@ -410,14 +472,14 @@ public function preRender(&$values) {
                   // item. We also use a dummy field object – either a clone of
                   // a fitting indexed field (to get its configuration), or a
                   // newly created one.
-                  $property_fields = \Drupal::getContainer()
-                    ->get('search_api.fields_helper')
+                  $property_fields = $this->getFieldsHelper()
                     ->filterForPropertyPath($index->getFields(), $datasource_id, $property_path);
                   if ($property_fields) {
                     $dummy_field = clone reset($property_fields);
                   }
                   else {
-                    $dummy_field = Utility::createFieldFromProperty($index, $definition, $datasource_id, $property_path, 'tmp', 'string');
+                    $dummy_field = $this->getFieldsHelper()
+                      ->createFieldFromProperty($index, $definition, $datasource_id, $property_path, 'tmp', 'string');
                   }
                   /** @var \Drupal\search_api\Item\ItemInterface $dummy_item */
                   $dummy_item = clone $row->_item;
@@ -438,7 +500,7 @@ public function preRender(&$values) {
                     if ($set_values) {
                       $row->{$combined_property_path}[] = $value;
                     }
-                    $typed_data = \Drupal::service('typed_data_manager')
+                    $typed_data = $this->getTypedDataManager()
                       ->create($definition, $value);
                     $row->_relationship_objects[$property_path][] = $typed_data;
                     $row->_relationship_parent_indices[$property_path][] = $j;
@@ -493,7 +555,8 @@ public function preRender(&$values) {
             // the relationship objects for the next iteration of the outer loop
             // over properties.
             foreach ($row->_relationship_objects[$property_path] as $typed_data) {
-              $row->{$combined_property_path}[] = Utility::extractFieldValues($typed_data);
+              $row->{$combined_property_path}[] = $this->getFieldsHelper()
+                ->extractFieldValues($typed_data);
             }
 
             // If we just set any field values on the result row, clean them up
diff --git a/src/Plugin/views/query/SearchApiQuery.php b/src/Plugin/views/query/SearchApiQuery.php
index 02338d09..72876766 100644
--- a/src/Plugin/views/query/SearchApiQuery.php
+++ b/src/Plugin/views/query/SearchApiQuery.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Database\Query\ConditionInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\search_api\Entity\Index;
@@ -112,12 +113,20 @@ class SearchApiQuery extends QueryPluginBase {
   protected $groupOperator = 'AND';
 
   /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|null
+   */
+  protected $moduleHandler;
+
+  /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
     /** @var static $plugin */
     $plugin = parent::create($container, $configuration, $plugin_id, $plugin_definition);
 
+    $plugin->setModuleHandler($container->get('module_handler'));
     $plugin->setLogger($container->get('logger.channel.search_api'));
 
     return $plugin;
@@ -149,6 +158,29 @@ public static function getIndexFromTable($table, EntityTypeManagerInterface $ent
   }
 
   /**
+   * Retrieves the module handler.
+   *
+   * @return \Drupal\Core\Extension\ModuleHandlerInterface
+   *   The module handler.
+   */
+  public function getModuleHandler() {
+    return $this->moduleHandler ?: \Drupal::moduleHandler();
+  }
+
+  /**
+   * Sets the module handler.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The new module handler.
+   *
+   * @return $this
+   */
+  public function setModuleHandler(ModuleHandlerInterface $module_handler) {
+    $this->moduleHandler = $module_handler;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
@@ -355,7 +387,7 @@ public function build(ViewExecutable $view) {
    * {@inheritdoc}
    */
   public function alter(ViewExecutable $view) {
-    \Drupal::moduleHandler()->invokeAll('views_query_alter', array($view, $this));
+    $this->getModuleHandler()->invokeAll('views_query_alter', array($view, $this));
   }
 
   /**
diff --git a/src/Plugin/views/relationship/SearchApiRelationship.php b/src/Plugin/views/relationship/SearchApiRelationship.php
index 4391c718..39868667 100644
--- a/src/Plugin/views/relationship/SearchApiRelationship.php
+++ b/src/Plugin/views/relationship/SearchApiRelationship.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\search_api\Plugin\views\relationship;
 
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\views\Plugin\views\relationship\RelationshipPluginBase;
 
@@ -15,6 +16,36 @@
 class SearchApiRelationship extends RelationshipPluginBase {
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|null
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Retrieves the entity type manager.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
+   *   The entity type manager.
+   */
+  public function getEntityTypeManager() {
+    return $this->entityTypeManager ?: \Drupal::entityTypeManager();
+  }
+
+  /**
+   * Sets the entity type manager.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The new entity type manager.
+   *
+   * @return $this
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function defineOptions() {
@@ -54,7 +85,8 @@ public function calculateDependencies() {
     $dependencies = array();
 
     if (!empty($this->definition['entity type'])) {
-      $entity_type = \Drupal::entityTypeManager()->getDefinition($this->definition['entity type']);
+      $entity_type = $this->getEntityTypeManager()
+        ->getDefinition($this->definition['entity type']);
       if ($entity_type) {
         $dependencies['module'][] = $entity_type->getProvider();
       }
diff --git a/src/Query/Query.php b/src/Query/Query.php
index 543fac1b..e00ad4bc 100644
--- a/src/Query/Query.php
+++ b/src/Query/Query.php
@@ -162,9 +162,16 @@ class Query implements QueryInterface {
   protected $parseModeManager;
 
   /**
+   * The display plugin manager.
+   *
+   * @var \Drupal\search_api\Display\DisplayPluginManager|null
+   */
+  protected $displayPluginManager;
+
+  /**
    * The result cache service.
    *
-   * @var \Drupal\search_api\Utility\QueryHelperInterface
+   * @var \Drupal\search_api\Utility\QueryHelperInterface|null
    */
   protected $queryHelper;
 
@@ -247,6 +254,29 @@ public function setParseModeManager(ParseModePluginManager $parse_mode_manager)
   }
 
   /**
+   * Retrieves the display plugin manager.
+   *
+   * @return \Drupal\search_api\Display\DisplayPluginManager
+   *   The display plugin manager.
+   */
+  public function getDisplayPluginManager() {
+    return $this->displayPluginManager ?: \Drupal::service('plugin.manager.search_api.display');
+  }
+
+  /**
+   * Sets the display plugin manager.
+   *
+   * @param \Drupal\search_api\Display\DisplayPluginManager $display_plugin_manager
+   *   The new display plugin manager.
+   *
+   * @return $this
+   */
+  public function setDisplayPluginManager($display_plugin_manager) {
+    $this->displayPluginManager = $display_plugin_manager;
+    return $this;
+  }
+
+  /**
    * Retrieves the query helper.
    *
    * @return \Drupal\search_api\Utility\QueryHelperInterface
@@ -292,8 +322,7 @@ public function setSearchId($search_id) {
    * {@inheritdoc}
    */
   public function getDisplayPlugin() {
-    $display_manager = \Drupal::getContainer()
-      ->get('plugin.manager.search_api.display');
+    $display_manager = $this->getDisplayPluginManager();
     if (isset($this->searchId) && $display_manager->hasDefinition($this->searchId)) {
       return $display_manager->createInstance($this->searchId);
     }
diff --git a/src/UnsavedConfigurationInterface.php b/src/UnsavedConfigurationInterface.php
index 932d4617..d0138a02 100644
--- a/src/UnsavedConfigurationInterface.php
+++ b/src/UnsavedConfigurationInterface.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\search_api;
 
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-
 /**
  * Represents a piece of configuration that was not permanently saved yet.
  */
@@ -40,15 +38,11 @@ public function isLocked();
   /**
    * Retrieves the owner of the lock on this configuration, if any.
    *
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity manager to use for loading the user, if necessary. Or NULL to
-   *   retrieve the entity manager from the global container.
-   *
    * @return \Drupal\user\UserInterface|null
    *   The lock's owner; or NULL if this object represents the still unchanged
    *   configuration that is currently stored.
    */
-  public function getLockOwner(EntityTypeManagerInterface $entity_type_manager = NULL);
+  public function getLockOwner();
 
   /**
    * Retrieves the last updated date of this configuration, if any.
@@ -73,12 +67,8 @@ public function setLockInformation($lock = NULL);
 
   /**
    * Saves the changes represented by this object permanently.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
-   *   The entity manager to use, if necessary. Or NULL to retrieve the entity
-   *   manager from the global container.
    */
-  public function savePermanent(EntityTypeManagerInterface $entity_type_manager = NULL);
+  public function savePermanent();
 
   /**
    * Discards the changes represented by this object.
diff --git a/src/UnsavedIndexConfiguration.php b/src/UnsavedIndexConfiguration.php
index 4e2a8039..7e7ec7bb 100644
--- a/src/UnsavedIndexConfiguration.php
+++ b/src/UnsavedIndexConfiguration.php
@@ -55,6 +55,13 @@ class UnsavedIndexConfiguration implements IndexInterface, UnsavedConfigurationI
   protected $lock;
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|null
+   */
+  protected $entityTypeManager;
+
+  /**
    * Constructs a new UnsavedIndexConfiguration.
    *
    * @param \Drupal\search_api\IndexInterface $index
@@ -72,6 +79,29 @@ public function __construct(IndexInterface $index, SharedTempStore $temp_store,
   }
 
   /**
+   * Retrieves the entity type manager.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
+   *   The entity type manager.
+   */
+  public function getEntityTypeManager() {
+    return $this->entityTypeManager ?: \Drupal::entityTypeManager();
+  }
+
+  /**
+   * Sets the entity type manager.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The new entity type manager.
+   *
+   * @return $this
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function setCurrentUserId($current_user_id) {
@@ -98,13 +128,12 @@ public function isLocked() {
   /**
    * {@inheritdoc}
    */
-  public function getLockOwner(EntityTypeManagerInterface $entity_type_manager = NULL) {
+  public function getLockOwner() {
     if (!$this->lock) {
       return NULL;
     }
     $uid = is_numeric($this->lock->owner) ? $this->lock->owner : 0;
-    $entity_type_manager = $entity_type_manager ?: \Drupal::entityTypeManager();
-    return $entity_type_manager->getStorage('user')->load($uid);
+    return $this->getEntityTypeManager()->getStorage('user')->load($uid);
   }
 
   /**
@@ -125,16 +154,14 @@ public function setLockInformation($lock = NULL) {
   /**
    * {@inheritdoc}
    */
-  public function savePermanent(EntityTypeManagerInterface $entity_type_manager = NULL) {
-    if (!$entity_type_manager) {
-      $entity_type_manager = \Drupal::entityTypeManager();
-    }
+  public function savePermanent() {
     // Make sure to overwrite only the index's fields, not just all properties.
     // Unlike the Views UI, we have several separate pages for editing index
     // entities, and only one of them is locked. Therefore, this extra step is
     // necessary, we can't just call $this->entity->save().
     /** @var \Drupal\search_api\IndexInterface $original */
-    $storage = $entity_type_manager->getStorage($this->entity->getEntityTypeId());
+    $storage = $this->getEntityTypeManager()
+      ->getStorage($this->entity->getEntityTypeId());
     $original = $storage->loadUnchanged($this->entity->id());
     $fields = $this->entity->getFields();
     // Set the correct index object on the field objects.
diff --git a/tests/src/Unit/Plugin/Processor/TestItemsTrait.php b/tests/src/Unit/Plugin/Processor/TestItemsTrait.php
index 2608c650..91e61be7 100644
--- a/tests/src/Unit/Plugin/Processor/TestItemsTrait.php
+++ b/tests/src/Unit/Plugin/Processor/TestItemsTrait.php
@@ -97,10 +97,10 @@ public function createItems(IndexInterface $index, $count, array $fields, Comple
         if (isset($field_info['datasource_id']) && $field_info['datasource_id'] != $datasource_id) {
           continue;
         }
-        $field_id = \Drupal::getContainer()
-          ->get('search_api.fields_helper')
-          ->getNewFieldId($index, $field_info['property_path']);
-        $field = Utility::createField($index, $field_id, $field_info);
+        $fields_helper = \Drupal::getContainer()
+          ->get('search_api.fields_helper');
+        $field_id = $fields_helper->getNewFieldId($index, $field_info['property_path']);
+        $field = $fields_helper->createField($index, $field_id, $field_info);
         $item->setField($field_id, $field);
       }
       $item->setFieldsExtracted(TRUE);
