From c972b3ee9d6ef7d1554480ea95d0bcc623d01c77 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole <catch@35733.no-reply.drupal.org>
Date: Tue, 20 May 2014 10:29:40 +0100
Subject: [PATCH] Issue #2190313 by pfrenssen, Berdir, amateescu, ianthomas_uk:
 Add $EntityType::load() and loadMultiple() to simplify loading entities.

---
 core/lib/Drupal/Core/Entity/Entity.php        |  70 ++++++
 .../Drupal/Core/Entity/EntityInterface.php    |  22 ++
 .../AmbiguousEntityClassException.php         |  34 +++
 .../NoCorrespondingEntityClassException.php   |  30 +++
 core/modules/aggregator/aggregator.module     |  25 +-
 .../aggregator/Tests/AggregatorTestBase.php   |   3 +-
 .../aggregator/Tests/UpdateFeedItemTest.php   |   3 +-
 core/modules/block/block.module               |  16 --
 .../block/custom_block/custom_block.module    |  28 ---
 .../custom_block/CustomBlockTypeForm.php      |   2 +-
 .../Tests/CustomBlockCreationTest.php         |   3 +-
 .../Tests/CustomBlockSaveTest.php             |   5 +-
 .../block/lib/Drupal/block/BlockForm.php      |   2 +-
 core/modules/breakpoint/breakpoint.module     |  20 +-
 .../breakpoint/Entity/BreakpointGroup.php     |   4 +-
 .../breakpoint/Tests/BreakpointCRUDTest.php   |   8 +-
 .../breakpoint/Tests/BreakpointTestBase.php   |   6 +-
 core/modules/comment/comment.module           |  18 +-
 core/modules/contact/contact.module           |  13 -
 .../lib/Drupal/contact/CategoryForm.php       |   2 +-
 core/modules/file/file.module                 |  16 +-
 core/modules/image/image.module               |  12 -
 core/modules/menu_link/menu_link.module       |  16 +-
 .../menu_ui/lib/Drupal/menu_ui/MenuForm.php   |   2 +-
 .../Drupal/menu_ui/Tests/MenuLanguageTest.php |   5 +-
 .../lib/Drupal/menu_ui/Tests/MenuTest.php     |   5 +-
 core/modules/menu_ui/menu_ui.module           |  12 -
 core/modules/node/node.module                 |  28 ++-
 .../ResponsiveImageMappingForm.php            |   2 +-
 .../responsive_image/responsive_image.module  |  22 +-
 core/modules/shortcut/shortcut.admin.inc      |   5 +-
 core/modules/shortcut/shortcut.module         |  21 +-
 core/modules/shortcut/src/ShortcutSetForm.php |   2 +-
 .../shortcut/src/Tests/ShortcutLinksTest.php  |   9 +-
 .../shortcut/src/Tests/ShortcutSetsTest.php   |   5 +-
 .../shortcut/src/Tests/ShortcutTestBase.php   |   3 +-
 core/modules/taxonomy/taxonomy.module         |  24 +-
 core/modules/user/user.module                 |  22 +-
 .../views_ui/lib/Drupal/views_ui/ViewUI.php   |  14 ++
 .../Tests/Core/Entity/EntityUnitTest.php      | 235 ++++++++++++++++++
 40 files changed, 558 insertions(+), 216 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php
 create mode 100644 core/lib/Drupal/Core/Entity/Exception/NoCorrespondingEntityClassException.php

diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index a3f1ef7c8693..b69ba1dfb209 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -13,6 +13,8 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
+use Drupal\Core\Entity\Exception\AmbiguousEntityClassException;
+use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
 use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
@@ -415,6 +417,74 @@ public function getListCacheTags() {
     return array($this->entityTypeId . 's' => TRUE);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function load($id) {
+    return \Drupal::entityManager()->getStorage(static::getEntityTypeFromStaticClass())->load($id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function loadMultiple(array $ids = NULL) {
+    return \Drupal::entityManager()->getStorage(static::getEntityTypeFromStaticClass())->loadMultiple($ids);
+  }
+
+  /**
+   * Returns the entity type ID based on the class that is called on.
+   *
+   * Compares the class this is called on against the known entity classes
+   * and returns the entity type ID of a direct match or a subclass as fallback,
+   * to support entity type definitions that were altered.
+   *
+   * @return string
+   *   The entity type ID.
+   *
+   * @throws \Drupal\Core\Entity\Exception\AmbiguousEntityClassException
+   *   Thrown when multiple subclasses correspond to the called class.
+   * @throws \Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException
+   *   Thrown when no entity class corresponds to the called class.
+   *
+   * @see \Drupal\Core\Entity\Entity::load()
+   * @see \Drupal\Core\Entity\Entity::loadMultiple()
+   */
+  protected static function getEntityTypeFromStaticClass() {
+    $called_class = get_called_class();
+    $subclasses = 0;
+    $same_class = 0;
+    $entity_type_id = NULL;
+    $subclass_entity_type_id = NULL;
+    foreach (\Drupal::entityManager()->getDefinitions() as $entity_type) {
+      // Check if this is the same class, throw an exception if there is more
+      // than one match.
+      if ($entity_type->getClass() == $called_class) {
+        $entity_type_id = $entity_type->id();
+        if ($same_class++) {
+          throw new AmbiguousEntityClassException($called_class);
+        }
+      }
+      // Check for entity types that are subclasses of the called class, but
+      // throw an exception if we have multiple matches.
+      elseif (is_subclass_of($entity_type->getClass(), $called_class)) {
+        $subclass_entity_type_id = $entity_type->id();
+        if ($subclasses++) {
+          throw new AmbiguousEntityClassException($called_class);
+        }
+      }
+    }
+
+    // Return the matching entity type ID or the subclass match if there is one
+    // as a secondary priority.
+    if ($entity_type_id) {
+      return $entity_type_id;
+    }
+    if ($subclass_entity_type_id) {
+      return $subclass_entity_type_id;
+    }
+    throw new NoCorrespondingEntityClassException($called_class);
+  }
+
   /**
    * Acts on an entity after it was saved or deleted.
    */
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index ab621441a100..4e483df13eda 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -171,6 +171,28 @@ public function hasLinkTemplate($key);
    */
   public function uriRelationships();
 
+  /**
+   * Loads an entity.
+   *
+   * @param mixed $id
+   *   The id of the entity to load.
+   *
+   * @return static
+   *   The entity object or NULL if there is no entity with the given ID.
+   */
+  public static function load($id);
+
+  /**
+   * Loads one or more entities.
+   *
+   * @param array $ids
+   *   An array of entity IDs, or NULL to load all entities.
+   *
+   * @return static[]
+   *   An array of entity objects indexed by their IDs.
+   */
+  public static function loadMultiple(array $ids = NULL);
+
   /**
    * Saves an entity permanently.
    *
diff --git a/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php b/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php
new file mode 100644
index 000000000000..3992bba00503
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Exception\AmbiguousEntityClassException.
+ */
+
+namespace Drupal\Core\Entity\Exception;
+
+/**
+ * Exception thrown if multiple subclasses exist for an entity.
+ *
+ * This might occur if an entity is subclassed multiple times and the base
+ * class is altered to use one of the subclasses instead. If a static method on
+ * the base class is then invoked it is impossible to determine which of the
+ * subclasses is responsible for it.
+ *
+ * @see hook_entity_info_alter()
+ * @see \Drupal\Core\Entity\Entity::getEntityTypeFromStaticClass()
+ */
+class AmbiguousEntityClassException extends \Exception {
+
+  /**
+   * Constructs an AmbiguousEntityClassException.
+   *
+   * @param string $class
+   *   The entity parent class.
+   */
+  public function __construct($class) {
+    $message = sprintf('Multiple subclasses provide an entity type for %s.', $class);
+    parent::__construct($message);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Exception/NoCorrespondingEntityClassException.php b/core/lib/Drupal/Core/Entity/Exception/NoCorrespondingEntityClassException.php
new file mode 100644
index 000000000000..fae821fb812f
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Exception/NoCorrespondingEntityClassException.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException.
+ */
+
+namespace Drupal\Core\Entity\Exception;
+
+/**
+ * Exception thrown if an entity type is not represented by a class.
+ *
+ * This might occur by calling a static method on an abstract class.
+ *
+ * @see \Drupal\Core\Entity\Entity::getEntityTypeFromStaticClass()
+ */
+class NoCorrespondingEntityClassException extends \Exception {
+
+  /**
+   * Constructs an NoCorrespondingEntityClassException.
+   *
+   * @param string $class
+   *   The class which does not correspond to an entity type.
+   */
+  public function __construct($class) {
+    $message = sprintf('The %s class does not correspond to an entity type.', $class);
+    parent::__construct($message);
+  }
+
+}
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index db175a6f3d4b..1fb502756581 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -5,6 +5,7 @@
  * Used to aggregate syndicated content (RSS, RDF, and Atom).
  */
 
+use Drupal\aggregator\Entity\Feed;
 use Drupal\aggregator\FeedInterface;
 use Drupal\Component\Utility\Xss;
 use Symfony\Component\HttpFoundation\Request;
@@ -113,9 +114,8 @@ function aggregator_permission() {
 function aggregator_cron() {
   $queue = \Drupal::queue('aggregator_feeds');
 
-  $result = \Drupal::entityManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh();
-  foreach ($result as $fid) {
-    $feed = aggregator_feed_load($fid);
+  $ids = \Drupal::entityManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh();
+  foreach (Feed::loadMultiple($ids) as $feed) {
     if ($queue->createItem($feed)) {
       // Add timestamp to avoid queueing item more than once.
       $feed->setQueuedTime(REQUEST_TIME);
@@ -124,12 +124,12 @@ function aggregator_cron() {
   }
 
   // Delete queued timestamp after 6 hours assuming the update has failed.
-  $result = \Drupal::entityQuery('aggregator_feed')
+  $ids = \Drupal::entityQuery('aggregator_feed')
     ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
     ->execute();
 
-  if ($result) {
-    $feeds = entity_load_multiple('aggregator_feed', $result);
+  if ($ids) {
+    $feeds = Feed::loadMultiple($ids);
     foreach ($feeds as $feed) {
       $feed->setQueuedTime(0);
       $feed->save();
@@ -153,19 +153,6 @@ function aggregator_queue_info() {
   return $queues;
 }
 
-/**
- * Loads an aggregator feed.
- *
- * @param int $fid
- *   The feed id.
- *
- * @return \Drupal\aggregator\FeedInterface
- *   An object describing the feed.
- */
-function aggregator_feed_load($fid) {
-  return entity_load('aggregator_feed', $fid);
-}
-
 /**
  * Renders the HTML content safely, as allowed.
  *
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
index 6a85657bc46f..f8df38ae654f 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/AggregatorTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\aggregator\Tests;
 
+use Drupal\aggregator\Entity\Feed;
 use Drupal\Core\Language\Language;
 use Drupal\simpletest\WebTestBase;
 use Drupal\aggregator\FeedInterface;
@@ -58,7 +59,7 @@ function createFeed($feed_url = NULL, array $edit = array()) {
 
     $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']))->fetchField();
     $this->assertTrue(!empty($fid), 'The feed found in database.');
-    return aggregator_feed_load($fid);
+    return Feed::load($fid);
   }
 
   /**
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
index 8256d66d699c..d53bdd17f61c 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Tests/UpdateFeedItemTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\aggregator\Tests;
+use Drupal\aggregator\Entity\Feed;
 
 /**
  * Tests functionality of updating a feed item in the Aggregator module.
@@ -48,7 +49,7 @@ function testUpdateFeedItem() {
     $this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed !name has been added.', array('!name' => $edit['title[0][value]'])));
 
     $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", array(':url' => $edit['url[0][value]']))->fetchField();
-    $feed = aggregator_feed_load($fid);
+    $feed = Feed::load($fid);
 
     $feed->refreshItems();
     $before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->id()))->fetchField();
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index 88b6ecc9e22f..37b93bd6d8b2 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -6,7 +6,6 @@
  */
 
 use Drupal\block\BlockInterface;
-use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\language\Entity\Language;
 use Drupal\system\Entity\Menu;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -295,21 +294,6 @@ function block_list($region) {
   return $blocks[$region];
 }
 
-/**
- * Loads a block instance.
- *
- * This should only be used when entity_load() cannot be used directly.
- *
- * @param string $entity_id
- *   The block ID.
- *
- * @return \Drupal\block\Entity\Block
- *   The loaded block object.
- */
-function block_load($entity_id) {
-  return entity_load('block', $entity_id);
-}
-
 /**
  * Implements hook_rebuild().
  */
diff --git a/core/modules/block/custom_block/custom_block.module b/core/modules/block/custom_block/custom_block.module
index 2c497542b24b..00caf8e5a1d4 100644
--- a/core/modules/block/custom_block/custom_block.module
+++ b/core/modules/block/custom_block/custom_block.module
@@ -5,8 +5,6 @@
  * Allows the creation of custom blocks through the user interface.
  */
 
-use Drupal\custom_block\Entity\CustomBlockType;
-use Drupal\custom_block\Entity\CustomBlock;
 use Symfony\Component\HttpFoundation\Request;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldInstanceConfig;
@@ -53,32 +51,6 @@ function custom_block_theme($existing, $type, $theme, $path) {
   );
 }
 
-/**
- * Loads a custom block type.
- *
- * @param int $id
- *   The ID of the custom block type to load.
- *
- * @return \Drupal\custom_block\Entity\CustomBlockType|null
- *   A CustomBlockType object or NULL if the requested $id does not exist.
- */
-function custom_block_type_load($id) {
-  return entity_load('custom_block_type', $id);
-}
-
-/**
- * Loads a custom block.
- *
- * @param int $id
- *   The id of the custom block.
- *
- * @return \Drupal\custom_block\Entity\CustomBlock|null
- *   A CustomBlock object or NULL if the requested $id does not exist.
- */
-function custom_block_load($id) {
-  return entity_load('custom_block', $id);
-}
-
 /**
  * Implements hook_entity_type_alter().
  */
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeForm.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeForm.php
index c790efab3b84..61567f805001 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeForm.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockTypeForm.php
@@ -35,7 +35,7 @@ public function form(array $form, array &$form_state) {
       '#type' => 'machine_name',
       '#default_value' => $block_type->id(),
       '#machine_name' => array(
-        'exists' => 'custom_block_type_load',
+        'exists' => '\Drupal\custom_block\Entity\CustomBlockType::load',
       ),
       '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
       '#disabled' => !$block_type->isNew(),
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
index 167014e9de09..0ae892a66339 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockCreationTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\custom_block\Tests;
 
 use Drupal\Core\Database\Database;
+use Drupal\custom_block\Entity\CustomBlock;
 
 /**
  * Tests creating and saving a block.
@@ -180,7 +181,7 @@ public function testBlockDelete() {
     $url = 'admin/structure/block/add/custom_block:' . $block->uuid() . '/' . \Drupal::config('system.theme')->get('default');
     $this->drupalPostForm($url, $instance, t('Save block'));
 
-    $block = custom_block_load(1);
+    $block = CustomBlock::load(1);
 
     // Test getInstances method.
     $this->assertEqual(1, count($block->getInstances()));
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockSaveTest.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockSaveTest.php
index ff0192e351ac..bae766abc8d2 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockSaveTest.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Tests/CustomBlockSaveTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\custom_block\Tests;
 
 use Drupal\Core\Language\Language;
+use Drupal\custom_block\Entity\CustomBlock;
 
 /**
  * Tests block save related functionality.
@@ -63,7 +64,7 @@ public function testImport() {
     $this->assertEqual($block->id(), $test_id, 'Block imported using provide id');
 
     // Test the import saved.
-    $block_by_id = custom_block_load($test_id);
+    $block_by_id = CustomBlock::load($test_id);
     $this->assertTrue($block_by_id, 'Custom block load by block ID.');
     $this->assertIdentical($block_by_id->body->value, $block_array['body']['value']);
   }
@@ -93,7 +94,7 @@ public function testDeterminingChanges() {
     $this->assertEqual($block->getChangedTime(), 979534800, 'Saving a custom block uses "changed" timestamp set in presave hook.');
 
     // Test the static block load cache to be cleared.
-    $block = custom_block_load($block->id());
+    $block = CustomBlock::load($block->id());
     $this->assertEqual($block->label(), 'updated_presave', 'Static cache has been cleared.');
   }
 
diff --git a/core/modules/block/lib/Drupal/block/BlockForm.php b/core/modules/block/lib/Drupal/block/BlockForm.php
index d614dcea114c..1249dab8c89b 100644
--- a/core/modules/block/lib/Drupal/block/BlockForm.php
+++ b/core/modules/block/lib/Drupal/block/BlockForm.php
@@ -86,7 +86,7 @@ public function form(array $form, array &$form_state) {
       '#description' => $this->t('A unique name for this block instance. Must be alpha-numeric and underscore separated.'),
       '#default_value' => !$entity->isNew() ? $entity->id() : $this->getUniqueMachineName($entity),
       '#machine_name' => array(
-        'exists' => 'block_load',
+        'exists' => '\Drupal\block\Entity\Block::load',
         'replace_pattern' => '[^a-z0-9_.]+',
         'source' => array('settings', 'label'),
       ),
diff --git a/core/modules/breakpoint/breakpoint.module b/core/modules/breakpoint/breakpoint.module
index 8d6283b9a887..5d358b3568d4 100644
--- a/core/modules/breakpoint/breakpoint.module
+++ b/core/modules/breakpoint/breakpoint.module
@@ -39,22 +39,6 @@ function breakpoint_help($route_name, Request $request) {
   }
 }
 
-/**
- * Load one breakpoint by its identifier.
- *
- * @param int $id
- *   The id of the breakpoint to load.
- *
- * @return \Drupal\breakpoint\Entity\Breakpoint|null
- *   The entity object, or NULL if there is no entity with the given id.
- *
- * @todo Remove this in a follow-up issue.
- * @see http://drupal.org/node/1798214
- */
-function breakpoint_load($id) {
-  return entity_load('breakpoint', $id);
-}
-
 /**
  * Load all breakpoint groups as select options.
  *
@@ -63,7 +47,7 @@ function breakpoint_load($id) {
  */
 function breakpoint_group_select_options() {
   $options = array();
-  $breakpoint_groups = entity_load_multiple('breakpoint_group');
+  $breakpoint_groups = BreakpointGroup::loadMultiple();
   foreach ($breakpoint_groups as $breakpoint_group) {
     $options[$breakpoint_group->id()] = $breakpoint_group->label();
   }
@@ -79,7 +63,7 @@ function breakpoint_group_select_options() {
  */
 function breakpoint_select_options() {
   $options = array();
-  $breakpoints = entity_load_multiple('breakpoint');
+  $breakpoints = Breakpoint::loadMultiple();
   foreach ($breakpoints as $breakpoint) {
     $options[$breakpoint->id()] = $breakpoint->label() . ' (' . $breakpoint->source . ' - ' . $breakpoint->sourceType . ') [' . $breakpoint->mediaQuery . ']';
   }
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
index c2426b7d9fcc..02d6e29e504e 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Entity/BreakpointGroup.php
@@ -157,7 +157,7 @@ public function isValid() {
    */
   public function addBreakpointFromMediaQuery($name, $media_query) {
     // Use the existing breakpoint if it exists.
-    $breakpoint = entity_load('breakpoint', $this->sourceType . '.' . $this->name . '.' . $name);
+    $breakpoint = Breakpoint::load($this->sourceType . '.' . $this->name . '.' . $name);
     if (!$breakpoint) {
       // Build a new breakpoint.
       $breakpoint = entity_create('breakpoint', array(
@@ -191,7 +191,7 @@ public function addBreakpoints($breakpoints) {
   public function getBreakpoints() {
     if (empty($this->breakpoints)) {
       foreach ($this->breakpoint_ids as $breakpoint_id) {
-        $breakpoint = breakpoint_load($breakpoint_id);
+        $breakpoint = Breakpoint::load($breakpoint_id);
         if ($breakpoint) {
           $this->breakpoints[$breakpoint_id] = $breakpoint;
         }
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
index 646100ac87f1..a9fcadf3ec4b 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointCRUDTest.php
@@ -37,10 +37,10 @@ public function testBreakpointCRUD() {
 
     $this->verifyBreakpoint($breakpoint);
 
-    // Test breakpoint_load_all
-    $all_breakpoints = entity_load_multiple('breakpoint');
+    // Test BreakPoint::loadMultiple().
+    $all_breakpoints = Breakpoint::loadMultiple();
     $config_name = $breakpoint->id();
-    $this->assertTrue(isset($all_breakpoints[$config_name]), 'breakpoint_load_all: New breakpoint is present when loading all breakpoints.');
+    $this->assertTrue(isset($all_breakpoints[$config_name]), 'New breakpoint is present when loading all breakpoints.');
     $this->verifyBreakpoint($breakpoint, $all_breakpoints[$config_name]);
 
     // Update the breakpoint.
@@ -51,6 +51,6 @@ public function testBreakpointCRUD() {
 
     // Delete the breakpoint.
     $breakpoint->delete();
-    $this->assertFalse(breakpoint_load($config_name), 'breakpoint_load: Loading a deleted breakpoint returns false.', 'Breakpoints API');
+    $this->assertNull(Breakpoint::load($config_name), 'Loading a deleted breakpoint returns null.', 'Breakpoints API');
   }
 }
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
index 89d0cc1bf563..aac96c94806d 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Tests/BreakpointTestBase.php
@@ -38,14 +38,14 @@ public function verifyBreakpoint(Breakpoint $breakpoint, Breakpoint $compare_bre
       'multipliers',
     );
 
-    // Verify breakpoint_load().
-    $compare_breakpoint = is_null($compare_breakpoint) ? breakpoint_load($breakpoint->id()) : $compare_breakpoint;
+    // Verify Breakpoint::load().
+    $compare_breakpoint = is_null($compare_breakpoint) ? Breakpoint::load($breakpoint->id()) : $compare_breakpoint;
     foreach ($properties as $property) {
       $t_args = array(
         '%breakpoint' => $breakpoint->label(),
         '%property' => $property,
       );
-      $this->assertEqual($compare_breakpoint->{$property}, $breakpoint->{$property}, format_string('breakpoint_load: Proper %property for breakpoint %breakpoint.', $t_args), 'Breakpoint API');
+      $this->assertEqual($compare_breakpoint->{$property}, $breakpoint->{$property}, format_string('Proper %property for breakpoint %breakpoint.', $t_args), 'Breakpoint API');
     }
   }
 }
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 18f0a575a94c..faaf6b6cd10e 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -11,6 +11,7 @@
  */
 
 use Drupal\comment\CommentInterface;
+use Drupal\comment\Entity\Comment;
 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\entity\Entity\EntityViewDisplay;
@@ -1049,8 +1050,8 @@ function comment_user_predelete($account) {
 /**
  * Loads comment entities from the database.
  *
- * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
- *   Use entity_load_multiple('comment', $cids).
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\comment\Entity\Comment::loadMultiple().
  *
  * @param array $cids
  *   (optional) An array of entity IDs. If omitted, all entities are loaded.
@@ -1064,7 +1065,10 @@ function comment_user_predelete($account) {
  * @see \Drupal\Core\Entity\Query\QueryInterface
  */
 function comment_load_multiple(array $cids = NULL, $reset = FALSE) {
-  return entity_load_multiple('comment', $cids, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('comment')->resetCache($cids);
+  }
+  return Comment::loadMultiple($cids);
 }
 
 /**
@@ -1077,9 +1081,15 @@ function comment_load_multiple(array $cids = NULL, $reset = FALSE) {
  *
  * @return \Drupal\comment\CommentInterface
  *   The comment object.
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\comment\Entity\Comment::load().
  */
 function comment_load($cid, $reset = FALSE) {
-  return entity_load('comment', $cid, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('comment')->resetCache(array($cid));
+  }
+  return Comment::load($cid);
 }
 
 /**
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index fe37a2b5693c..4f4edaef86d9 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -115,19 +115,6 @@ function contact_entity_extra_field_info() {
   return $fields;
 }
 
-/**
- * Loads a contact category.
- *
- * @param $id
- *   The ID of the contact category to load.
- *
- * @return \Drupal\contact\Entity\Category|null
- *   A Category object or NULL if the requested $id does not exist.
- */
-function contact_category_load($id) {
-  return entity_load('contact_category', $id);
-}
-
 /**
  * Implements hook_mail().
  */
diff --git a/core/modules/contact/lib/Drupal/contact/CategoryForm.php b/core/modules/contact/lib/Drupal/contact/CategoryForm.php
index a810b1e576eb..14c893d8b718 100644
--- a/core/modules/contact/lib/Drupal/contact/CategoryForm.php
+++ b/core/modules/contact/lib/Drupal/contact/CategoryForm.php
@@ -37,7 +37,7 @@ public function form(array $form, array &$form_state) {
       '#default_value' => $category->id(),
       '#maxlength' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
       '#machine_name' => array(
-        'exists' => 'contact_category_load',
+        'exists' => '\Drupal\contact\Entity\Category::load',
       ),
       '#disabled' => !$category->isNew(),
     );
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 7580d9b6ff6c..47b72ef16655 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -83,13 +83,19 @@ function file_element_info() {
  * @return array
  *   An array of file entities, indexed by fid.
  *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\file\Entity\File::loadMultiple().
+ *
  * @see hook_file_load()
  * @see file_load()
  * @see entity_load()
  * @see \Drupal\Core\Entity\Query\EntityQueryInterface
  */
 function file_load_multiple(array $fids = NULL, $reset = FALSE) {
-  return entity_load_multiple('file', $fids, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('file')->resetCache($fids);
+  }
+  return File::loadMultiple($fids);
 }
 
 /**
@@ -103,11 +109,17 @@ function file_load_multiple(array $fids = NULL, $reset = FALSE) {
  * @return \Drupal\file\FileInterface
  *   A file entity or NULL if the file was not found.
  *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\file\Entity\File::load().
+ *
  * @see hook_file_load()
  * @see file_load_multiple()
  */
 function file_load($fid, $reset = FALSE) {
-  return entity_load('file', $fid, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('file')->resetCache(array($fid));
+  }
+  return File::load($fid);
 }
 
 /**
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 9c9e82cd5e9f..fc6f319668b3 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -7,8 +7,6 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\field\Entity\FieldConfig;
-use Drupal\field\Entity\FieldInstanceConfig;
 use Drupal\file\Entity\File;
 use Drupal\field\FieldConfigInterface;
 use Drupal\field\FieldInstanceConfigInterface;
@@ -245,16 +243,6 @@ function image_path_flush($path) {
   }
 }
 
-/**
- * Loads an ImageStyle object.
- *
- * @param string $name
- *   The ID of the ImageStyle object to load.
- */
-function image_style_load($name) {
-  return entity_load('image_style', $name);
-}
-
 /**
  * Gets an array of image styles suitable for using as select list options.
  *
diff --git a/core/modules/menu_link/menu_link.module b/core/modules/menu_link/menu_link.module
index ee273d82be17..ac3c408f8429 100644
--- a/core/modules/menu_link/menu_link.module
+++ b/core/modules/menu_link/menu_link.module
@@ -45,9 +45,15 @@ function menu_link_uri(MenuLink $menu_link) {
  *
  * @return \Drupal\menu_link\Entity\MenuLink|null
  *   A menu link entity, or NULL if there is no entity with the given ID.
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\menu_link\Entity\MenuLink::load().
  */
 function menu_link_load($mlid = NULL, $reset = FALSE) {
-  return entity_load('menu_link', $mlid, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('menu_link')->resetCache(array($mlid));
+  }
+  return MenuLink::load($mlid);
 }
 
 /**
@@ -63,9 +69,15 @@ function menu_link_load($mlid = NULL, $reset = FALSE) {
  *
  * @see menu_link_load()
  * @see entity_load_multiple()
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\menu_link\Entity\MenuLink::loadMultiple().
  */
 function menu_link_load_multiple(array $mlids = NULL, $reset = FALSE) {
-  return entity_load_multiple('menu_link', $mlids, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('menu_link')->resetCache($mlids);
+  }
+  return MenuLink::loadMultiple($mlids);
 }
 
 /**
diff --git a/core/modules/menu_ui/lib/Drupal/menu_ui/MenuForm.php b/core/modules/menu_ui/lib/Drupal/menu_ui/MenuForm.php
index a796f66174c8..4913b8307e5e 100644
--- a/core/modules/menu_ui/lib/Drupal/menu_ui/MenuForm.php
+++ b/core/modules/menu_ui/lib/Drupal/menu_ui/MenuForm.php
@@ -238,7 +238,7 @@ public function save(array $form, array &$form_state) {
    *
    * This form constructor can be integrated as a section into another form. It
    * relies on the following keys in $form_state:
-   * - menu: A loaded menu definition, as returned by menu_ui_load().
+   * - menu: A menu entity.
    * - menu_overview_form_parents: An array containing the parent keys to this
    *   form.
    * Forms integrating this section should call menu_overview_form_submit() from
diff --git a/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuLanguageTest.php b/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuLanguageTest.php
index 73374aa4b784..971e821bf636 100644
--- a/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuLanguageTest.php
+++ b/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuLanguageTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Language\Language;
+use Drupal\system\Entity\Menu;
 
 /**
  * Defines a test class for testing menu language functionality.
@@ -174,7 +175,7 @@ function testMenuLanguageRemovedEnglish() {
     $this->drupalPostForm('admin/structure/menu/add', $edit, t('Save'));
 
     // Check that the language settings were saved.
-    $menu = menu_ui_load($menu_name);
+    $menu = Menu::load($menu_name);
     $this->assertEqual($menu->langcode, 'en');
 
     // Remove English language. To do that another language has to be set as
@@ -186,7 +187,7 @@ function testMenuLanguageRemovedEnglish() {
 
     // Save the menu again and check if the language is still the same.
     $this->drupalPostForm("admin/structure/menu/manage/$menu_name", array(), t('Save'));
-    $menu = menu_ui_load($menu_name);
+    $menu = Menu::load($menu_name);
     $this->assertEqual($menu->langcode, 'en');
   }
 
diff --git a/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuTest.php b/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuTest.php
index 078e26a91a5c..e8f822236930 100644
--- a/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuTest.php
+++ b/core/modules/menu_ui/lib/Drupal/menu_ui/Tests/MenuTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\menu_ui\Tests;
 
 use Drupal\Component\Serialization\Json;
+use Drupal\system\Entity\Menu;
 
 /**
  * Defines a test class for testing menu and menu link functionality.
@@ -199,7 +200,7 @@ function addCustomMenu() {
 
     // Enable the block.
     $this->drupalPlaceBlock('system_menu_block:' . $menu_name);
-    return menu_ui_load($menu_name);
+    return Menu::load($menu_name);
   }
 
   /**
@@ -216,7 +217,7 @@ function deleteCustomMenu() {
     $this->drupalPostForm("admin/structure/menu/manage/$menu_name/delete", array(), t('Delete'));
     $this->assertResponse(200);
     $this->assertRaw(t('The custom menu %title has been deleted.', array('%title' => $label)), 'Custom menu was deleted');
-    $this->assertFalse(menu_ui_load($menu_name), 'Custom menu was deleted');
+    $this->assertNull(Menu::load($menu_name), 'Custom menu was deleted');
     // Test if all menu links associated to the menu were removed from database.
     $result = entity_load_multiple_by_properties('menu_link', array('menu_name' => $menu_name));
     $this->assertFalse($result, 'All menu links associated to the custom menu were deleted.');
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index b12baee4db3b..3639d26c19f2 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -113,18 +113,6 @@ function menu_ui_theme() {
   );
 }
 
-/**
- * Load the data for a single custom menu.
- *
- * @param $menu_name
- *   The unique name of a custom menu to load.
- * @return
- *   Array defining the custom menu, or NULL if the menu doesn't exist.
- */
-function menu_ui_load($menu_name) {
-  return entity_load('menu', $menu_name);
-}
-
 /**
  * Implements hook_menu_insert()
  */
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 0f9b94152ebd..2253f71e08ab 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -19,6 +19,8 @@
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldInstanceConfig;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
 use Drupal\node\NodeTypeInterface;
 use Drupal\node\NodeInterface;
 use Drupal\Core\Entity\EntityInterface;
@@ -295,10 +297,13 @@ function node_mark($nid, $timestamp) {
  * @return \Drupal\node\NodeTypeInterface[]
  *   An array of node type entities, keyed by ID.
  *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\node\Entity\NodeType::loadMultiple().
+ *
  * @see node_type_load()
  */
 function node_type_get_types() {
-  return entity_load_multiple('node_type');
+  return NodeType::loadMultiple();
 }
 
 /**
@@ -353,9 +358,12 @@ function node_type_get_description(NodeTypeInterface $node_type) {
  *
  * @return \Drupal\node\NodeTypeInterface
  *   A node type object or NULL if $name does not exist.
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\node\Entity\NodeType::load().
  */
 function node_type_load($name) {
-  return entity_load('node_type', $name);
+  return NodeType::load($name);
 }
 
 /**
@@ -487,11 +495,17 @@ function node_type_update_nodes($old_id, $new_id) {
  * @return \Drupal\node\NodeInterface[]
  *   An array of node entities indexed by nid.
  *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\node\Entity\Node::loadMultiple().
+ *
  * @see entity_load_multiple()
  * @see \Drupal\Core\Entity\Query\EntityQueryInterface
  */
 function node_load_multiple(array $nids = NULL, $reset = FALSE) {
-  return entity_load_multiple('node', $nids, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('node')->resetCache($nids);
+  }
+  return Node::loadMultiple($nids);
 }
 
 /**
@@ -505,9 +519,15 @@ function node_load_multiple(array $nids = NULL, $reset = FALSE) {
  *
  * @return \Drupal\node\NodeInterface|null
  *   A fully-populated node entity, or NULL if the node is not found.
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\node\Entity\Node::load().
  */
 function node_load($nid = NULL, $reset = FALSE) {
-  return entity_load('node', $nid, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('node')->resetCache(array($nid));
+  }
+  return Node::load($nid);
 }
 
 /**
diff --git a/core/modules/responsive_image/lib/Drupal/responsive_image/ResponsiveImageMappingForm.php b/core/modules/responsive_image/lib/Drupal/responsive_image/ResponsiveImageMappingForm.php
index 1b4bb4a89d75..ff62862b59a8 100644
--- a/core/modules/responsive_image/lib/Drupal/responsive_image/ResponsiveImageMappingForm.php
+++ b/core/modules/responsive_image/lib/Drupal/responsive_image/ResponsiveImageMappingForm.php
@@ -50,7 +50,7 @@ public function form(array $form, array &$form_state) {
       '#type' => 'machine_name',
       '#default_value' => $responsive_image_mapping->id(),
       '#machine_name' => array(
-        'exists' => 'responsive_image_mapping_load',
+        'exists' => '\Drupal\responsive_image\Entity\ResponsiveImageMapping::load',
         'source' => array('label'),
       ),
       '#disabled' => (bool) $responsive_image_mapping->id() && $this->operation != 'duplicate',
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 82ecafef81ea..53c182c01001 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -5,7 +5,7 @@
  * Responsive image display formatter for image fields.
  */
 
-use Drupal\responsive_image\Entity\ResponsiveImageMapping;
+use Drupal\breakpoint\Entity\Breakpoint;
 use \Drupal\Core\Template\Attribute;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -70,24 +70,6 @@ function responsive_image_menu() {
   return $items;
 }
 
-/**
- * Load one responsive image by its identifier.
- *
- * @param int $id
- *   The id of the responsive image mapping to load.
- *
- * @return \Drupal\responsive_image\ResponsiveImageMappingInterface
- *   The entity object, or NULL if there is no entity with the given id.
- *
- * @todo Needed for menu_callback
- *
- * @see http://drupal.org/node/1798214
- *
- */
-function responsive_image_mapping_load($id) {
-  return entity_load('responsive_image_mapping', $id);
-}
-
 /**
  * Implements hook_theme().
  */
@@ -218,7 +200,7 @@ function theme_responsive_image($variables) {
 
   // All breakpoints and multipliers.
   foreach ($variables['breakpoints'] as $breakpoint_name => $multipliers) {
-    $breakpoint = breakpoint_load($breakpoint_name);
+    $breakpoint = Breakpoint::load($breakpoint_name);
     if ($breakpoint) {
       $new_sources = array();
       foreach ($multipliers as $multiplier => $image_style) {
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index e6f3a86ca09d..f2f938d68f83 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -4,6 +4,7 @@
  * @file
  * Administrative page callbacks for the shortcut module.
  */
+use Drupal\shortcut\Entity\ShortcutSet;
 
 /**
  * Form callback: builds the form for switching shortcut sets.
@@ -71,7 +72,7 @@ function shortcut_set_switch($form, &$form_state, $account = NULL) {
     $form['id'] = array(
       '#type' => 'machine_name',
       '#machine_name' => array(
-        'exists' => 'shortcut_set_load',
+        'exists' => '\Drupal\shortcut\Entity\ShortcutSet::load',
         'source' => array('label'),
         'replace_pattern' => '[^a-z0-9-]+',
         'replace' => '-',
@@ -164,7 +165,7 @@ function shortcut_set_switch_submit($form, &$form_state) {
   }
   else {
     // Switch to a different shortcut set.
-    $set = shortcut_set_load($form_state['values']['set']);
+    $set = ShortcutSet::load($form_state['values']['set']);
     $replacements = array(
       '%user' => $account->getUsername(),
       '%set_name' => $set->label(),
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 3ad21f00aff1..09ead751fc25 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -9,6 +9,7 @@
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Routing\UrlMatcher;
 use Drupal\Core\Url;
+use Drupal\shortcut\Entity\ShortcutSet;
 use Drupal\shortcut\ShortcutSetInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -121,22 +122,6 @@ function shortcut_set_switch_access($account = NULL) {
   return FALSE;
 }
 
-/**
- * Loads the data for a shortcut set.
- *
- * @param string $id
- *   The machine-name of the shortcut set to load.
- *
- * @return \Drupal\shortcut\ShortcutSetInterface|null
- *   If the shortcut set exists, an object containing the following properties:
- *   - 'id': The internal name of the shortcut set.
- *   - 'label': The title of the shortcut set.
- *   If the shortcut set does not exist, the function returns NULL.
- */
-function shortcut_set_load($id) {
-  return entity_load('shortcut_set', $id);
-}
-
 /**
  * Assigns a user to a particular shortcut set.
  *
@@ -198,7 +183,7 @@ function shortcut_current_displayed_set($account = NULL) {
     ->getStorage('shortcut_set')
     ->getAssignedToUser($account);
   if ($shortcut_set_name) {
-    $shortcut_set = shortcut_set_load($shortcut_set_name);
+    $shortcut_set = ShortcutSet::load($shortcut_set_name);
   }
   // Otherwise, use the default set.
   else {
@@ -233,7 +218,7 @@ function shortcut_default_set($account = NULL) {
   $suggestions = array_reverse(\Drupal::moduleHandler()->invokeAll('shortcut_default_set', array($account)));
   $suggestions[] = 'default';
   foreach ($suggestions as $name) {
-    if ($shortcut_set = shortcut_set_load($name)) {
+    if ($shortcut_set = ShortcutSet::load($name)) {
       break;
     }
   }
diff --git a/core/modules/shortcut/src/ShortcutSetForm.php b/core/modules/shortcut/src/ShortcutSetForm.php
index 3dd628294756..2b50ed2eee0f 100644
--- a/core/modules/shortcut/src/ShortcutSetForm.php
+++ b/core/modules/shortcut/src/ShortcutSetForm.php
@@ -31,7 +31,7 @@ public function form(array $form, array &$form_state) {
     $form['id'] = array(
       '#type' => 'machine_name',
       '#machine_name' => array(
-        'exists' => 'shortcut_set_load',
+        'exists' => '\Drupal\shortcut\Entity\ShortcutSet::load',
         'source' => array('label'),
         'replace_pattern' => '[^a-z0-9-]+',
         'replace' => '-',
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index 6607b7f732c3..a713cfcbd8ad 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\shortcut\Tests;
+use Drupal\shortcut\Entity\ShortcutSet;
 
 /**
  * Defines shortcut links test cases.
@@ -60,7 +61,7 @@ public function testShortcutLinkAdd() {
       );
       $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
       $this->assertResponse(200);
-      $saved_set = shortcut_set_load($set->id());
+      $saved_set = ShortcutSet::load($set->id());
       $paths = $this->getShortcutInformation($saved_set, 'path');
       $this->assertTrue(in_array($this->container->get('path.alias_manager')->getPathByAlias($test['path']), $paths), 'Shortcut created: ' . $test['path']);
       $this->assertLink($title, 0, 'Shortcut link found on the page.');
@@ -110,7 +111,7 @@ public function testShortcutLinkRename() {
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
     $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name, 'path' => $shortcut->path->value), t('Save'));
-    $saved_set = shortcut_set_load($set->id());
+    $saved_set = ShortcutSet::load($set->id());
     $titles = $this->getShortcutInformation($saved_set, 'title');
     $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
     $this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.');
@@ -128,7 +129,7 @@ public function testShortcutLinkChangePath() {
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
     $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $shortcut->getTitle(), 'path' => $new_link_path), t('Save'));
-    $saved_set = shortcut_set_load($set->id());
+    $saved_set = ShortcutSet::load($set->id());
     $paths = $this->getShortcutInformation($saved_set, 'path');
     $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
     $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.');
@@ -156,7 +157,7 @@ public function testShortcutLinkDelete() {
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
     $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id() . '/delete', array(), 'Delete');
-    $saved_set = shortcut_set_load($set->id());
+    $saved_set = ShortcutSet::load($set->id());
     $ids = $this->getShortcutInformation($saved_set, 'id');
     $this->assertFalse(in_array($shortcut->id(), $ids), 'Successfully deleted a shortcut.');
 
diff --git a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
index 2a31535e75d9..c17316d2bdad 100644
--- a/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutSetsTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\shortcut\Tests;
+use Drupal\shortcut\Entity\ShortcutSet;
 
 /**
  * Defines shortcut set test cases.
@@ -98,7 +99,7 @@ function testShortcutSetRename() {
     $this->drupalGet('admin/config/user-interface/shortcut');
     $this->clickLink(t('Edit shortcut set'));
     $this->drupalPostForm(NULL, array('label' => $new_label), t('Save'));
-    $set = shortcut_set_load($set->id());
+    $set = ShortcutSet::load($set->id());
     $this->assertTrue($set->label() == $new_label, 'Shortcut set has been successfully renamed.');
   }
 
@@ -110,7 +111,7 @@ function testShortcutSetRenameAlreadyExists() {
     $existing_label = $this->set->label();
     $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id(), array('label' => $existing_label), t('Save'));
     $this->assertRaw(t('The shortcut set %name already exists. Choose another name.', array('%name' => $existing_label)));
-    $set = shortcut_set_load($set->id());
+    $set = ShortcutSet::load($set->id());
     $this->assertNotEqual($set->label(), $existing_label, format_string('The shortcut set %title cannot be renamed to %new-title because a shortcut set with that title already exists.', array('%title' => $set->label(), '%new-title' => $existing_label)));
   }
 
diff --git a/core/modules/shortcut/src/Tests/ShortcutTestBase.php b/core/modules/shortcut/src/Tests/ShortcutTestBase.php
index de9e11030a32..461a40cc78e9 100644
--- a/core/modules/shortcut/src/Tests/ShortcutTestBase.php
+++ b/core/modules/shortcut/src/Tests/ShortcutTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\shortcut\Tests;
 
+use Drupal\shortcut\Entity\ShortcutSet;
 use Drupal\shortcut\ShortcutSetInterface;
 use Drupal\simpletest\WebTestBase;
 
@@ -79,7 +80,7 @@ function setUp() {
 
     // Log in as admin and grab the default shortcut set.
     $this->drupalLogin($this->admin_user);
-    $this->set = shortcut_set_load('default');
+    $this->set = ShortcutSet::load('default');
     shortcut_set_assign_user($this->set, $this->admin_user);
   }
 
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 4577e7ea1487..fde291e76ef5 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -593,8 +593,8 @@ function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) {
  * @see entity_load_multiple()
  * @see \Drupal\Core\Entity\Query\EntityQueryInterface
  *
- * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
- *   Use entity_load_multiple('taxonomy_term', $tids).
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\taxonomy\Entity\Term::loadMultiple().
  *
  * @param array $tids
  *   (optional) An array of entity IDs. If omitted, all entities are loaded.
@@ -604,7 +604,7 @@ function taxonomy_term_load_multiple_by_name($name, $vocabulary = NULL) {
  *   found, an empty array is returned.
  */
 function taxonomy_term_load_multiple(array $tids = NULL) {
-  return entity_load_multiple('taxonomy_term', $tids);
+  return Term::loadMultiple($tids);
 }
 
 /**
@@ -616,8 +616,8 @@ function taxonomy_term_load_multiple(array $tids = NULL) {
  *
  * @see entity_load_multiple()
  *
- * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
- *   Use entity_load_multiple('taxonomy_vocabulary', $vids).
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\taxonomy\Entity\Vocabulary::loadMultiple().
  *
  * @param array $vids
  *   (optional) An array of entity IDs. If omitted, all entities are loaded.
@@ -626,14 +626,14 @@ function taxonomy_term_load_multiple(array $tids = NULL) {
  *  An array of vocabulary objects, indexed by vid.
  */
 function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
-  return entity_load_multiple('taxonomy_vocabulary', $vids);
+  return Vocabulary::loadMultiple($vids);
 }
 
 /**
  * Return the taxonomy vocabulary entity matching a vocabulary ID.
  *
- * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
- *   Use entity_load('taxonomy_vocabulary', $vid).
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\taxonomy\Entity\Vocabulary::load().
  *
  * @param int $vid
  *   The vocabulary's ID.
@@ -643,14 +643,14 @@ function taxonomy_vocabulary_load_multiple(array $vids = NULL) {
  *   statically cached.
  */
 function taxonomy_vocabulary_load($vid) {
-  return entity_load('taxonomy_vocabulary', $vid);
+  return Vocabulary::load($vid);
 }
 
 /**
  * Return the taxonomy term entity matching a term ID.
  *
- * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
- *   Use entity_load('taxonomy_term', $tid).
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\taxonomy\Entity\Term::load().
  *
  * @param $tid
  *   A term's ID
@@ -663,7 +663,7 @@ function taxonomy_term_load($tid) {
   if (!is_numeric($tid)) {
     return NULL;
   }
-  return entity_load('taxonomy_term', $tid);
+  return Term::load($tid);
 }
 
 /**
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 3fe1f1c6759c..4b28c8165e9a 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -8,6 +8,7 @@
 use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Url;
 use Drupal\file\Entity\File;
+use Drupal\user\Entity\Role;
 use Drupal\user\Entity\User;
 use Drupal\user\UserInterface;
 use Drupal\user\RoleInterface;
@@ -261,9 +262,15 @@ function user_entity_extra_field_info() {
  * @see user_load_by_mail()
  * @see user_load_by_name()
  * @see \Drupal\Core\Entity\Query\QueryInterface
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\user\Entity\User::loadMultiple().
  */
 function user_load_multiple(array $uids = NULL, $reset = FALSE) {
-  return entity_load_multiple('user', $uids, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('user')->resetCache($uids);
+  }
+  return User::loadMultiple($uids);
 }
 
 /**
@@ -288,10 +295,16 @@ function user_load_multiple(array $uids = NULL, $reset = FALSE) {
  *   A fully-loaded user object upon successful user load, or NULL if the user
  *   cannot be loaded.
  *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\user\Entity\User::load().
+ *
  * @see user_load_multiple()
  */
 function user_load($uid, $reset = FALSE) {
-  return entity_load('user', $uid, $reset);
+  if ($reset) {
+    \Drupal::entityManager()->getStorage('user')->resetCache(array($uid));
+  }
+  return User::load($uid);
 }
 
 /**
@@ -1293,9 +1306,12 @@ function user_roles($membersonly = FALSE, $permission = NULL) {
  * @return
  *   A fully-loaded role object if a role with the given ID exists, or NULL
  *   otherwise.
+ *
+ * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
+ *   Use \Drupal\user\Entity\Role::load().
  */
 function user_role_load($rid) {
-  return entity_load('user_role', $rid);
+  return Role::load($rid);
 }
 
 /**
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
index 0aabe8545b01..e1e668032e4b 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -889,6 +889,20 @@ public function createDuplicate() {
     return $this->storage->createDuplicate();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function load($id) {
+    return View::load($id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function loadMultiple(array $ids = NULL) {
+    return View::loadMultiple($ids);
+  }
+
   /**
    * Implements \Drupal\Core\Entity\EntityInterface::delete().
    */
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index 6ccad149eeb0..9e600c6cdb77 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -8,7 +8,10 @@
 namespace Drupal\Tests\Core\Entity;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Entity\Entity;
 use Drupal\Core\Language\Language;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestMul;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -239,6 +242,238 @@ public function testLanguage() {
     $this->assertSame('en', $this->entity->language()->id);
   }
 
+  /**
+   * Setup for the tests of the ::load() method.
+   */
+  function setupTestLoad() {
+    // Use an entity type object which has the methods enabled which are being
+    // called by the protected method Entity::getEntityTypeFromStaticClass().
+    $methods = get_class_methods('Drupal\Core\Entity\EntityType');
+    unset($methods[array_search('getClass', $methods)]);
+    unset($methods[array_search('setClass', $methods)]);
+    $this->entityType = $this->getMockBuilder('\Drupal\Core\Entity\EntityType')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->getMock();
+
+    // Base our mocked entity on a real entity class so we can test if calling
+    // Entity::load() on the base class will bubble up to an actual entity.
+    $this->entityTypeId = 'entity_test_mul';
+    $methods = get_class_methods('Drupal\entity_test\Entity\EntityTestMul');
+    unset($methods[array_search('load', $methods)]);
+    unset($methods[array_search('loadMultiple', $methods)]);
+    $this->entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTestMul')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->getMock();
+    $this->entityType->setClass(get_class($this->entity));
+
+    $this->entityManager->expects($this->once())
+      ->method('getDefinitions')
+      ->will($this->returnValue(array($this->entityTypeId => $this->entityType)));
+
+    $this->entityType->expects($this->any())
+      ->method('id')
+      ->will($this->returnValue($this->entityTypeId));
+  }
+
+  /**
+   * @covers ::load
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests Entity::load() when called statically on the Entity base class.
+   */
+  public function testLoad() {
+    $this->setupTestLoad();
+
+    $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+    $storage->expects($this->once())
+      ->method('load')
+      ->with(1)
+      ->will($this->returnValue($this->entity));
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with($this->entityTypeId)
+      ->will($this->returnValue($storage));
+
+    // Call Entity::load statically and check that it returns the mock entity.
+    $this->assertSame($this->entity, Entity::load(1));
+  }
+
+  /**
+   * @covers ::load
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests if an assertion is thrown if Entity::load() is called on a base class
+   * which is subclassed multiple times.
+   *
+   * @expectedException \Drupal\Core\Entity\Exception\AmbiguousEntityClassException
+   */
+  public function testLoadWithAmbiguousSubclasses() {
+    // Use an entity type object which has the methods enabled which are being
+    // called by the protected method Entity::getEntityTypeFromStaticClass().
+    $methods = get_class_methods('Drupal\Core\Entity\EntityType');
+    unset($methods[array_search('getClass', $methods)]);
+    unset($methods[array_search('setClass', $methods)]);
+
+    $first_entity_type = $this->getMockBuilder('\Drupal\Core\Entity\EntityType')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->getMock();
+    $first_entity_type->setClass('Drupal\entity_test\Entity\EntityTestMul');
+
+    $second_entity_type = $this->getMockBuilder('\Drupal\Core\Entity\EntityType')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->setMockClassName($this->randomName())
+      ->getMock();
+    $second_entity_type->setClass('Drupal\entity_test\Entity\EntityTestMulRev');
+
+    $this->entityManager->expects($this->once())
+      ->method('getDefinitions')
+      ->will($this->returnValue(array(
+        'entity_test_mul' => $first_entity_type,
+        'entity_test_mul_rev' => $second_entity_type,
+      )));
+
+    // Call Entity::load statically and check that it throws an exception.
+    Entity::load(1);
+  }
+
+  /**
+   * @covers ::load
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests if an assertion is thrown if Entity::load() is called on a class
+   * that matches multiple times.
+   *
+   * @expectedException \Drupal\Core\Entity\Exception\AmbiguousEntityClassException
+   */
+  public function testLoadWithAmbiguousClasses() {
+    // Use an entity type object which has the methods enabled which are being
+    // called by the protected method Entity::getEntityTypeFromStaticClass().
+    $methods = get_class_methods('Drupal\Core\Entity\EntityType');
+    unset($methods[array_search('getClass', $methods)]);
+    unset($methods[array_search('setClass', $methods)]);
+
+    $first_entity_type = $this->getMockBuilder('\Drupal\Core\Entity\EntityType')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->getMock();
+    $first_entity_type->setClass('Drupal\entity_test\Entity\EntityTest');
+
+    $second_entity_type = $this->getMockBuilder('\Drupal\Core\Entity\EntityType')
+      ->disableOriginalConstructor()
+      ->setMethods($methods)
+      ->setMockClassName($this->randomName())
+      ->getMock();
+    $second_entity_type->setClass('Drupal\entity_test\Entity\EntityTest');
+
+    $this->entityManager->expects($this->once())
+      ->method('getDefinitions')
+      ->will($this->returnValue(array(
+        'entity_test_mul' => $first_entity_type,
+        'entity_test_mul_rev' => $second_entity_type,
+      )));
+
+    // Call EntityTest::load() statically and check that it throws an exception.
+    EntityTest::load(1);
+  }
+
+  /**
+   * @covers ::load
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests if an assertion is thrown if Entity::load() is called and there are
+   * no subclasses defined that can return entities.
+   *
+   * @expectedException \Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException
+   */
+  public function testLoadWithNoCorrespondingSubclasses() {
+    $this->entityManager->expects($this->once())
+      ->method('getDefinitions')
+      ->will($this->returnValue(array()));
+
+    // Call Entity::load statically and check that it throws an exception.
+    Entity::load(1);
+  }
+
+  /**
+   * @covers ::load
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests Entity::load() when called statically on a subclass of Entity.
+   */
+  public function testLoadSubClass() {
+    $this->setupTestLoad();
+
+    $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+    $storage->expects($this->once())
+      ->method('load')
+      ->with(1)
+      ->will($this->returnValue($this->entity));
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with($this->entityTypeId)
+      ->will($this->returnValue($storage));
+
+    // Call Entity::load statically on the subclass and check that it returns
+    // the mock entity.
+    $class = get_class($this->entity);
+    $this->assertSame($this->entity, $class::load(1));
+  }
+
+  /**
+   * @covers ::loadMultiple
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests Entity::loadMultiple() when called statically on the Entity base
+   * class.
+   */
+  public function testLoadMultiple() {
+    $this->setupTestLoad();
+
+    $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+    $storage->expects($this->once())
+      ->method('loadMultiple')
+      ->with(array(1))
+      ->will($this->returnValue(array(1 => $this->entity)));
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with($this->entityTypeId)
+      ->will($this->returnValue($storage));
+
+    // Call Entity::loadMultiple statically and check that it returns the mock
+    // entity.
+    $this->assertSame(array(1 => $this->entity), Entity::loadMultiple(array(1)));
+  }
+
+  /**
+   * @covers ::loadMultiple
+   * @covers ::getEntityTypeFromStaticClass
+   *
+   * Tests Entity::loadMultiple() when called statically on a subclass of
+   * Entity.
+   */
+  public function testLoadMultipleSubClass() {
+    $this->setupTestLoad();
+
+    $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
+    $storage->expects($this->once())
+      ->method('loadMultiple')
+      ->with(array(1))
+      ->will($this->returnValue(array(1 => $this->entity)));
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with($this->entityTypeId)
+      ->will($this->returnValue($storage));
+
+    // Call Entity::loadMultiple statically and check that it returns the mock
+    // entity.
+    $class = get_class($this->entity);
+    $this->assertSame(array(1 => $this->entity), $class::loadMultiple(array(1)));
+  }
+
   /**
    * @covers ::save
    */
-- 
GitLab