diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php index a6ba4084ae..1d956f551f 100644 --- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php @@ -108,8 +108,8 @@ public function __construct(EntityTypeInterface $entity_type, MemoryCacheInterfa * Retrieves the class name used to create the entity. * * @param string $bundle - * A specific entity type bundle identifier. Note: depending on how the - * entity is configured, a bundle may not be provided. + * (optional) A specific entity type bundle identifier. Can be omitted in + * the case of entity types without bundles, like User. * * @return string * A class name. @@ -364,16 +364,7 @@ protected function preLoad(array &$ids = NULL) { * Associative array of query results, keyed on the entity ID. */ protected function postLoad(array &$entities) { - $entity_classes = []; - - /** @var \Drupal\Core\Entity\EntityInterface $entity */ - foreach ($entities as $id => &$entity) { - $entity_class = get_class($entity); - if (!isset($entity_classes[$entity_class])) { - $entity_classes[$entity_class] = []; - } - $entity_classes[$entity_class][$id] = &$entity; - } + $entity_classes = $this->getEntityClasses($entities); foreach ($entity_classes as $entity_class => &$items) { $entity_class::postLoad($this, $entities); @@ -405,6 +396,7 @@ protected function mapFromStorageRecords(array $records) { $entities = []; foreach ($records as $record) { $entity_class = $this->getEntityClass(); + /* @var $entity \Drupal\Core\Entity\EntityInterface */ $entity = new $entity_class($record, $this->entityTypeId); $entities[$entity->id()] = $entity; } @@ -420,6 +412,7 @@ protected function mapFromStorageRecords(array $records) { * The entity being saved. * * @return bool + * TRUE if this entity exists in storage, FALSE otherwise. */ abstract protected function has($id, EntityInterface $entity); @@ -438,16 +431,7 @@ public function delete(array $entities) { $keyed_entities[$entity->id()] = $entity; } - $entity_classes = []; - - /** @var \Drupal\Core\Entity\EntityInterface $entity */ - foreach ($keyed_entities as $id => $entity) { - $entity_class = get_class($entity); - if (!isset($entity_classes[$entity_class])) { - $entity_classes[$entity_class] = []; - } - $entity_classes[$entity_class][$id] = $entity; - } + $entity_classes = $this->getEntityClasses($keyed_entities); // Allow code to run before deleting. foreach ($entity_classes as $entity_class => &$items) { @@ -650,4 +634,27 @@ public function getAggregateQuery($conjunction = 'AND') { */ abstract protected function getQueryServiceName(); + /** + * Return an array of entities indexed by class name and ID. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The array of entities to index. + * + * @return array + * An array of the passed-in entities, indexed by their class name and ID. + */ + protected function getEntityClasses(array $entities) { + $entity_classes = []; + + foreach ($entities as $id => &$entity) { + $entity_class = get_class($entity); + if (!isset($entity_classes[$entity_class])) { + $entity_classes[$entity_class] = []; + } + $entity_classes[$entity_class][$id] = &$entity; + } + + return $entity_classes; + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeRepository.php b/core/lib/Drupal/Core/Entity/EntityTypeRepository.php index 97d4ab2c7d..bfd25b6f82 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeRepository.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeRepository.php @@ -78,46 +78,112 @@ public function getEntityTypeFromClass($class_name) { return $this->classNameEntityTypeMap[$class_name]; } + if ($entity_type_id = $this->getExactEntityTypeFromClass($class_name)) { + $this->classNameEntityTypeMap[$class_name] = $entity_type_id; + return $entity_type_id; + } + + throw new NoCorrespondingEntityClassException($class_name); + } + + /** + * {@inheritdoc} + */ + public function clearCachedDefinitions() { + $this->classNameEntityTypeMap = []; + } + + /** + * Return the matching entity type ID if there is one. + * + * @param string $class_name + * The fully-qualified class name to look up. + * + * @return string|null + * The entity type ID, or NULL if one does not exist. + */ + protected function getExactEntityTypeFromClass($class_name) { $same_class = 0; + + // Even if an exact match is returned, we need to check if a subclass is + // also defined so we can throw an AmbiguousEntityClassException. + $exact = $this->getEntityTypeFromExactClass($class_name, $same_class); + $subclass = $this->getEntityTypeFromSubclass($class_name, $same_class); + + return $exact ?: $subclass; + } + + /** + * Get the entity type ID, if an exact class name match exists. + * + * @param string $class_name + * The fully-qualified class name to look up. + * @param int &$same_class + * A counter to keep track of if multiple classes implement the same entity + * type. + * + * @return string|null + * The entity type ID, or NULL if one does not exist. + */ + protected function getEntityTypeFromExactClass($class_name, &$same_class) { $entity_type_id = NULL; $definitions = $this->entityTypeManager->getDefinitions(); // Attempt to find an exact entity class name match. foreach ($definitions as $entity_type) { if ($class_name === $entity_type->getClass() || $class_name === $entity_type->getOriginalClass()) { - $entity_type_id = $entity_type->id(); - if ($same_class++) { - throw new AmbiguousEntityClassException($class_name); - } + $entity_type_id = $this->getUnambiguousEntityType($class_name, $same_class, $entity_type); } } + return $entity_type_id; + } - // Attempt to find a subclassed entity class name match. - if (!$entity_type_id) { - foreach ($definitions as $entity_type) { - if (is_subclass_of($class_name, $entity_type->getClass()) || is_subclass_of($class_name, $entity_type->getOriginalClass())) { - $entity_type_id = $entity_type->id(); - if ($same_class++) { - throw new AmbiguousEntityClassException($class_name); - } - } + /** + * Get the entity type ID, if a subclass exists. + * + * @param string $class_name + * The fully-qualified class name to look up. + * @param int &$same_class + * A counter to keep track of if multiple classes implement the same entity + * type. + * + * @return string|null + * The entity type ID, or NULL if one does not exist. + */ + protected function getEntityTypeFromSubclass($class_name, int &$same_class) { + $definitions = $this->entityTypeManager->getDefinitions(); + $entity_type_id = NULL; + foreach ($definitions as $entity_type) { + if (is_subclass_of($class_name, $entity_type->getClass()) || is_subclass_of($class_name, $entity_type->getOriginalClass())) { + $entity_type_id = $this->getUnambiguousEntityType($class_name, $same_class, $entity_type); } } - - // 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); + return $entity_type_id; } /** - * {@inheritdoc} + * Get the entity type ID, ensuring only a single class implements the bundle. + * + * @param string $class_name + * The fully-qualified class name to look up. + * @param int &$same_class + * A counter to keep track of if multiple classes implement the same entity + * type. + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The entity type corresponding to the class name. + * + * @return string + * The entity type ID. + * + * @throws \Drupal\Core\Entity\Exception\AmbiguousEntityClassException + * Thrown if multiple classes implement the same entity type and bundle. */ - public function clearCachedDefinitions() { - $this->classNameEntityTypeMap = []; + private function getUnambiguousEntityType($class_name, int &$same_class, EntityTypeInterface $entity_type) { + if ($same_class++) { + throw new AmbiguousEntityClassException($class_name); + } + + return $entity_type->id(); } }