diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 452f8c7100bdb79dfff2c666a1e1f4f4f1006795..cf2583dee119de9c2b95d8a5bf9da3828aedae51 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -424,76 +424,26 @@ public function getListCacheTags() { * {@inheritdoc} */ public static function load($id) { - return \Drupal::entityManager()->getStorage(static::getEntityTypeFromStaticClass())->load($id); + $entity_manager = \Drupal::entityManager(); + return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->load($id); } /** * {@inheritdoc} */ public static function loadMultiple(array $ids = NULL) { - return \Drupal::entityManager()->getStorage(static::getEntityTypeFromStaticClass())->loadMultiple($ids); + $entity_manager = \Drupal::entityManager(); + return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->loadMultiple($ids); } /** * {@inheritdoc} */ public static function create(array $values = array()) { - return \Drupal::entityManager()->getStorage(static::getEntityTypeFromStaticClass())->create($values); + $entity_manager = \Drupal::entityManager(); + return $entity_manager->getStorage($entity_manager->getEntityTypeFromClass(get_called_class()))->create($values); } - /** - * 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/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 7b56b682517cd4f70297fcf68ce05b3d85a69a99..8cfb35c0f43758abdc0933fbd9052467328e48f4 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -14,6 +14,8 @@ use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Entity\Exception\AmbiguousEntityClassException; +use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageInterface; @@ -144,6 +146,13 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa */ protected $fieldMapByFieldType = array(); + /** + * Contains cached mappings of class names to entity types. + * + * @var array + */ + protected $classNameEntityTypeMap = array(); + /** * Constructs a new Entity plugin manager. * @@ -180,6 +189,7 @@ public function clearCachedDefinitions() { parent::clearCachedDefinitions(); $this->clearCachedBundles(); $this->clearCachedFieldDefinitions(); + $this->classNameEntityTypeMap = array(); } /** @@ -933,4 +943,34 @@ public function loadEntityByUuid($entity_type_id, $uuid) { return reset($entities); } + /** + * {@inheritdoc} + */ + public function getEntityTypeFromClass($class_name) { + + // Check the already calculated classes first. + if (isset($this->classNameEntityTypeMap[$class_name])) { + return $this->classNameEntityTypeMap[$class_name]; + } + + $same_class = 0; + $entity_type_id = NULL; + foreach ($this->getDefinitions() as $entity_type) { + if ($entity_type->getOriginalClass() == $class_name || $entity_type->getClass() == $class_name) { + $entity_type_id = $entity_type->id(); + if ($same_class++) { + throw new AmbiguousEntityClassException($class_name); + } + } + } + + // Return the matching entity type ID if there is one. + if ($entity_type_id) { + $this->classNameEntityTypeMap[$class_name] = $entity_type_id; + return $entity_type_id; + } + + throw new NoCorrespondingEntityClassException($class_name); + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php index e4d6b051c11f17d6d05f821117e9d729537126f8..f5c2374ea2a5ea4178887ec325b4ff51ef8e7a67 100644 --- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php @@ -388,4 +388,27 @@ public function getFormModeOptions($entity_type_id, $include_disabled = FALSE); */ public function loadEntityByUuid($entity_type_id, $uuid); + /** + * 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. + * + * @param string $class_name + * Class name to use for searching the entity type ID. + * + * @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() + */ + public function getEntityTypeFromClass($class_name); + } diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index ca245055d86f0f6e60a71aab6f7eba1edc8bb82c..aa78adbebae72555890fe2f33296f4a1c39360e3 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -70,6 +70,15 @@ class EntityType implements EntityTypeInterface { */ protected $class; + /** + * The name of the original entity type class. + * + * This is only set if the class name is changed. + * + * @var string + */ + protected $originalClass; + /** * An array of controllers. * @@ -308,10 +317,22 @@ public function getClass() { return $this->class; } + /** + * {@inheritdoc} + */ + public function getOriginalClass() { + return $this->originalClass ?: $this->class; + } + /** * {@inheritdoc} */ public function setClass($class) { + if (!$this->originalClass && $this->class) { + // If the original class is currently not set, set it to the current + // class, assume that is the original class name. + $this->originalClass = $this->class; + } $this->class = $class; return $this; } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php index c4b0496e0676f60ac399ba884b629daa76b7038a..af4bc13e389fea2b86c2dfe4f13964ae0fea8703 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php @@ -74,6 +74,18 @@ public function getProvider(); */ public function getClass(); + /** + * Returns the name of the original entity type class. + * + * In case the class name was changed with setClass(), this will return + * the initial value. Useful when trying to identify the entity type ID based + * on the class. + * + * @return string + * The name of the original entity type class. + */ + public function getOriginalClass(); + /** * Returns an array of entity keys. * diff --git a/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php b/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php index 3992bba0050375b97652b3cf54b1c2baf79f8284..c809c32d32b3fa1951f06776474471da7480dd34 100644 --- a/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php +++ b/core/lib/Drupal/Core/Entity/Exception/AmbiguousEntityClassException.php @@ -8,12 +8,7 @@ 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. + * Exception thrown if multiple entity types exist for an entity class. * * @see hook_entity_info_alter() * @see \Drupal\Core\Entity\Entity::getEntityTypeFromStaticClass() @@ -27,7 +22,7 @@ class AmbiguousEntityClassException extends \Exception { * The entity parent class. */ public function __construct($class) { - $message = sprintf('Multiple subclasses provide an entity type for %s.', $class); + $message = sprintf('Multiple entity types found for %s.', $class); parent::__construct($message); } diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 94644266946c397177197a08b068c0f4bd14d5fb..7238abe7263879989e035a8edce776455fab7242 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -326,22 +326,19 @@ function hook_node_grants_alter(&$grants, \Drupal\Core\Session\AccountInterface function hook_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Session\AccountInterface $account, $langcode) { $type = is_string($node) ? $node : $node->getType(); - $configured_types = node_permissions_get_configured_types(); - if (isset($configured_types[$type])) { - if ($op == 'create' && $account->hasPermission('create ' . $type . ' content')) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'create' && $account->hasPermission('create ' . $type . ' content')) { + return NODE_ACCESS_ALLOW; + } - if ($op == 'update') { - if ($account->hasPermission('edit any ' . $type . ' content', $account) || ($account->hasPermission('edit own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'update') { + if ($account->hasPermission('edit any ' . $type . ' content', $account) || ($account->hasPermission('edit own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))) { + return NODE_ACCESS_ALLOW; } + } - if ($op == 'delete') { - if ($account->hasPermission('delete any ' . $type . ' content', $account) || ($account->hasPermission('delete own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'delete') { + if ($account->hasPermission('delete any ' . $type . ' content', $account) || ($account->hasPermission('delete own ' . $type . ' content') && ($account->id() == $node->getOwnerId()))) { + return NODE_ACCESS_ALLOW; } } diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 6029104bc45b7530f12e8b65934deba00829ed58..ba529e0c7f36efb9f7840caeb7bfe1ccca538312 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -716,8 +716,8 @@ function node_permission() { ), ); - // Generate standard node permissions for all applicable node types. - foreach (node_permissions_get_configured_types() as $type) { + // Generate node permissions for all node types. + foreach (NodeType::loadMultiple() as $type) { $perms += node_list_permissions($type); } @@ -1158,22 +1158,19 @@ function node_form_system_themes_admin_form_submit($form, FormStateInterface $fo function node_node_access(NodeInterface $node, $op, $account) { $type = $node->bundle(); - $configured_types = node_permissions_get_configured_types(); - if (isset($configured_types[$type])) { - if ($op == 'create' && $account->hasPermission('create ' . $type . ' content', $account)) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'create' && $account->hasPermission('create ' . $type . ' content', $account)) { + return NODE_ACCESS_ALLOW; + } - if ($op == 'update') { - if ($account->hasPermission('edit any ' . $type . ' content', $account) || ($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'update') { + if ($account->hasPermission('edit any ' . $type . ' content', $account) || ($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))) { + return NODE_ACCESS_ALLOW; } + } - if ($op == 'delete') { - if ($account->hasPermission('delete any ' . $type . ' content', $account) || ($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))) { - return NODE_ACCESS_ALLOW; - } + if ($op == 'delete') { + if ($account->hasPermission('delete any ' . $type . ' content', $account) || ($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))) { + return NODE_ACCESS_ALLOW; } } @@ -1222,31 +1219,6 @@ function node_list_permissions($type) { return $perms; } -/** - * Returns an array of node types that should be managed by permissions. - * - * By default, this will include all node types in the system. To exclude a - * specific node from getting permissions defined for it, set the - * node_permissions_$type variable to 0. Core does not provide an interface for - * doing so. However, contrib modules may exclude their own nodes in - * hook_install(). Alternatively, contrib modules may configure all node types - * at once, or decide to apply some other hook_node_access() implementation to - * some or all node types. - * - * @return - * An array of node types managed by this module. - */ -function node_permissions_get_configured_types() { - $configured_types = array(); - foreach (node_type_get_types() as $name => $type) { - $node_settings = $type->getModuleSettings('node'); - if (!isset($node_settings['permissions']) || !empty($node_settings['permissions'])) { - $configured_types[$name] = $type; - } - } - return $configured_types; -} - /** * Fetches an array of permission IDs granted to the given user ID. * diff --git a/core/modules/node/src/Access/NodeAddAccessCheck.php b/core/modules/node/src/Access/NodeAddAccessCheck.php index 3813f9f0144c2dab63e2caaf815e13115dabdbd6..89b1fd542c7d52f8914ac6e2792cb8f8bf8a750a 100644 --- a/core/modules/node/src/Access/NodeAddAccessCheck.php +++ b/core/modules/node/src/Access/NodeAddAccessCheck.php @@ -53,7 +53,7 @@ public function access(AccountInterface $account, NodeTypeInterface $node_type = return $access_control_handler->createAccess($node_type->id(), $account) ? static::ALLOW : static::DENY; } // If checking whether a node of any type may be created. - foreach (node_permissions_get_configured_types() as $node_type) { + foreach ($this->entityManager->getStorage('node_type')->loadMultiple() as $node_type) { if ($access_control_handler->createAccess($node_type->id(), $account)) { return static::ALLOW; } diff --git a/core/modules/node/src/NodeAccessControlHandler.php b/core/modules/node/src/NodeAccessControlHandler.php index ff136b855a2c504808dd0134173f67790f990d12..3bd534e517503e184cfb2d055ef621dd297981c1 100644 --- a/core/modules/node/src/NodeAccessControlHandler.php +++ b/core/modules/node/src/NodeAccessControlHandler.php @@ -122,10 +122,7 @@ protected function checkAccess(EntityInterface $node, $operation, $langcode, Acc * {@inheritdoc} */ protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) { - $configured_types = node_permissions_get_configured_types(); - if (isset($configured_types[$entity_bundle])) { - return $account->hasPermission('create ' . $entity_bundle . ' content'); - } + return $account->hasPermission('create ' . $entity_bundle . ' content'); } /** diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index 7ec941e6314a73961ea5bc99e99289ef32874391..6022ecf2df15b6cb54bcf06df84d9d26ed2da6e0 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -1254,6 +1254,87 @@ public function testGetFieldMapByFieldType() { $this->assertArrayNotHasKey('id', $stringFields['test_entity_type']); } + /** + * @covers ::getEntityTypeFromClass() + */ + public function testGetEntityTypeFromClass() { + $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $apple->expects($this->exactly(2)) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\apple\Entity\Apple')); + $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $banana->expects($this->exactly(2)) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\banana\Entity\Banana')); + $banana->expects($this->once()) + ->method('getClass') + ->will($this->returnValue('\Drupal\mango\Entity\Mango')); + $banana->expects($this->exactly(2)) + ->method('id') + ->will($this->returnValue('banana')); + $this->setUpEntityManager(array( + 'apple' => $apple, + 'banana' => $banana, + )); + + $entity_type_id = $this->entityManager->getEntityTypeFromClass('\Drupal\banana\Entity\Banana'); + $this->assertSame('banana', $entity_type_id); + $entity_type_id = $this->entityManager->getEntityTypeFromClass('\Drupal\mango\Entity\Mango'); + $this->assertSame('banana', $entity_type_id); + } + + /** + * @covers ::getEntityTypeFromClass() + * + * @expectedException \Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException + * @expectedExceptionMessage The \Drupal\pear\Entity\Pear class does not correspond to an entity type. + */ + public function testGetEntityTypeFromClassNoMatch() { + $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $apple->expects($this->once()) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\apple\Entity\Apple')); + $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $banana->expects($this->once()) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\banana\Entity\Banana')); + $this->setUpEntityManager(array( + 'apple' => $apple, + 'banana' => $banana, + )); + + $this->entityManager->getEntityTypeFromClass('\Drupal\pear\Entity\Pear'); + } + + /** + * @covers ::getEntityTypeFromClass() + * + * @expectedException \Drupal\Core\Entity\Exception\AmbiguousEntityClassException + * @expectedExceptionMessage Multiple entity types found for \Drupal\apple\Entity\Apple. + */ + public function testGetEntityTypeFromClassAmbiguous() { + $boskoop = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $boskoop->expects($this->once()) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\apple\Entity\Apple')); + $boskoop->expects($this->once()) + ->method('id') + ->will($this->returnValue('boskop')); + $gala = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); + $gala->expects($this->once()) + ->method('getOriginalClass') + ->will($this->returnValue('\Drupal\apple\Entity\Apple')); + $gala->expects($this->once()) + ->method('id') + ->will($this->returnValue('gala')); + $this->setUpEntityManager(array( + 'boskoop' => $boskoop, + 'gala' => $gala, + )); + + $this->entityManager->getEntityTypeFromClass('\Drupal\apple\Entity\Apple'); + } + /** * Gets a mock controller class name. * diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php index 82c93216b38e72f537c68b3fad4223cc6b94fe9f..eb6d936db5c8b0173f6108d3618f5534d69ea53c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php @@ -202,6 +202,26 @@ public function testIdExceedsMaxLength() { $this->setUpEntityType(array('id' => $id)); } + /** + * @covers ::getOriginalClass + */ + public function testgetOriginalClassUnchanged() { + $class = $this->randomMachineName(); + $entity_type = $this->setUpEntityType(array('class' => $class)); + $this->assertEquals($class, $entity_type->getOriginalClass()); + } + + /** + * @covers ::setClass + * @covers ::getOriginalClass + */ + public function testgetOriginalClassChanged() { + $class = $this->randomMachineName(); + $entity_type = $this->setUpEntityType(array('class' => $class)); + $entity_type->setClass($this->randomMachineName()); + $this->assertEquals($class, $entity_type->getOriginalClass()); + } + /** * @covers ::id */ diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php index a6626f6760d79eda6f39c92cca5baabad2e6ce4a..e8004d10b9bc4eb21d126d2906980530433875ee 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php @@ -9,6 +9,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\Entity; +use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException; use Drupal\Core\Language\Language; use Drupal\entity_test\Entity\EntityTest; use Drupal\entity_test\Entity\EntityTestMul; @@ -234,16 +235,6 @@ public function testLanguage() { * 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'; @@ -255,146 +246,23 @@ function setupTestLoad() { ->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. + * Tests Entity::load() when called statically on a subclass of Entity. */ 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->randomMachineName()) - ->getMock(); - $second_entity_type->setClass('Drupal\entity_test\Entity\EntityTestMulRev'); + $class_name = get_class($this->entity); $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->randomMachineName()) - ->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(); + ->method('getEntityTypeFromClass') + ->with($class_name) + ->willReturn($this->entityTypeId); $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); $storage->expects($this->once()) @@ -406,46 +274,26 @@ public function testLoadSubClass() { ->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)); + // Call Entity::load statically and check that it returns the mock entity. + $this->assertSame($this->entity, $class_name::load(1)); } /** * @covers ::loadMultiple * @covers ::getEntityTypeFromStaticClass * - * Tests Entity::loadMultiple() when called statically on the Entity base - * class. + * Tests Entity::loadMultiple() when called statically on a subclass of + * Entity. */ 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))); - } + $class_name = get_class($this->entity); - /** - * @covers ::loadMultiple - * @covers ::getEntityTypeFromStaticClass - * - * Tests Entity::loadMultiple() when called statically on a subclass of - * Entity. - */ - public function testLoadMultipleSubClass() { - $this->setupTestLoad(); + $this->entityManager->expects($this->once()) + ->method('getEntityTypeFromClass') + ->with($class_name) + ->willReturn($this->entityTypeId); $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); $storage->expects($this->once()) @@ -459,8 +307,7 @@ public function testLoadMultipleSubClass() { // 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))); + $this->assertSame(array(1 => $this->entity), $class_name::loadMultiple(array(1))); } /** @@ -470,6 +317,12 @@ public function testLoadMultipleSubClass() { public function testCreate() { $this->setupTestLoad(); + $class_name = get_class($this->entity); + $this->entityManager->expects($this->once()) + ->method('getEntityTypeFromClass') + ->with($class_name) + ->willReturn($this->entityTypeId); + $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface'); $storage->expects($this->once()) ->method('create') @@ -482,8 +335,7 @@ public function testCreate() { // Call Entity::create() statically and check that it returns the mock // entity. - $class = get_class($this->entity); - $this->assertSame($this->entity, $class::create(array())); + $this->assertSame($this->entity, $class_name::create(array())); } /**