Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
22.73% |
5 / 22 |
CRAP | |
48.74% |
58 / 119 |
| EntityStorageBase | |
0.00% |
0 / 1 |
|
37.04% |
10 / 27 |
597.60 | |
48.74% |
58 / 119 |
| __construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
|||
| getEntityTypeId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getEntityType | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| loadUnchanged | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| resetCache | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 5 |
|||
| getFromStaticCache | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 4 |
|||
| setStaticCache | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
| invokeHook | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| create | |
100.00% |
1 / 1 |
4 | |
100.00% |
9 / 9 |
|||
| doCreate | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| load | |
100.00% |
1 / 1 |
2 | |
100.00% |
2 / 2 |
|||
| loadMultiple | |
0.00% |
0 / 1 |
14.25 | |
75.00% |
15 / 20 |
|||
| doLoadMultiple | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| postLoad | |
0.00% |
0 / 1 |
3.79 | |
55.56% |
5 / 9 |
|||
| mapFromStorageRecords | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
| has | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| delete | |
100.00% |
1 / 1 |
5 | |
100.00% |
15 / 15 |
|||
| doDelete | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| save | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
| doPreSave | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 11 |
|||
| doSave | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| doPostSave | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 7 |
|||
| buildPropertyQuery | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
| loadByProperties | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| getQuery | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getAggregateQuery | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getQueryServiceName | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Entity\EntityStorageBase. | |
| */ | |
| namespace Drupal\Core\Entity; | |
| use Drupal\Core\Entity\Query\QueryInterface; | |
| /** | |
| * A base entity storage class. | |
| */ | |
| abstract class EntityStorageBase extends EntityHandlerBase implements EntityStorageInterface, EntityHandlerInterface { | |
| /** | |
| * Static cache of entities, keyed by entity ID. | |
| * | |
| * @var array | |
| */ | |
| protected $entities = array(); | |
| /** | |
| * Entity type ID for this storage. | |
| * | |
| * @var string | |
| */ | |
| protected $entityTypeId; | |
| /** | |
| * Information about the entity type. | |
| * | |
| * The following code returns the same object: | |
| * @code | |
| * \Drupal::entityManager()->getDefinition($this->entityTypeId) | |
| * @endcode | |
| * | |
| * @var \Drupal\Core\Entity\EntityTypeInterface | |
| */ | |
| protected $entityType; | |
| /** | |
| * Name of the entity's ID field in the entity database table. | |
| * | |
| * @var string | |
| */ | |
| protected $idKey; | |
| /** | |
| * Name of entity's UUID database table field, if it supports UUIDs. | |
| * | |
| * Has the value FALSE if this entity does not use UUIDs. | |
| * | |
| * @var string | |
| */ | |
| protected $uuidKey; | |
| /** | |
| * The name of the entity langcode property. | |
| * | |
| * @var string | |
| */ | |
| protected $langcodeKey; | |
| /** | |
| * The UUID service. | |
| * | |
| * @var \Drupal\Component\Uuid\UuidInterface | |
| */ | |
| protected $uuidService; | |
| /** | |
| * Name of the entity class. | |
| * | |
| * @var string | |
| */ | |
| protected $entityClass; | |
| /** | |
| * Constructs an EntityStorageBase instance. | |
| * | |
| * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | |
| * The entity type definition. | |
| */ | |
| public function __construct(EntityTypeInterface $entity_type) { | |
| $this->entityTypeId = $entity_type->id(); | |
| $this->entityType = $entity_type; | |
| $this->idKey = $this->entityType->getKey('id'); | |
| $this->uuidKey = $this->entityType->getKey('uuid'); | |
| $this->langcodeKey = $this->entityType->getKey('langcode'); | |
| $this->entityClass = $this->entityType->getClass(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEntityTypeId() { | |
| return $this->entityTypeId; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getEntityType() { | |
| return $this->entityType; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function loadUnchanged($id) { | |
| $this->resetCache(array($id)); | |
| return $this->load($id); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function resetCache(array $ids = NULL) { | |
| if ($this->entityType->isStaticallyCacheable() && isset($ids)) { | |
| foreach ($ids as $id) { | |
| unset($this->entities[$id]); | |
| } | |
| } | |
| else { | |
| $this->entities = array(); | |
| } | |
| } | |
| /** | |
| * Gets entities from the static cache. | |
| * | |
| * @param array $ids | |
| * If not empty, return entities that match these IDs. | |
| * | |
| * @return \Drupal\Core\Entity\EntityInterface[] | |
| * Array of entities from the entity cache. | |
| */ | |
| protected function getFromStaticCache(array $ids) { | |
| $entities = array(); | |
| // Load any available entities from the internal cache. | |
| if ($this->entityType->isStaticallyCacheable() && !empty($this->entities)) { | |
| $entities += array_intersect_key($this->entities, array_flip($ids)); | |
| } | |
| return $entities; | |
| } | |
| /** | |
| * Stores entities in the static entity cache. | |
| * | |
| * @param \Drupal\Core\Entity\EntityInterface[] $entities | |
| * Entities to store in the cache. | |
| */ | |
| protected function setStaticCache(array $entities) { | |
| if ($this->entityType->isStaticallyCacheable()) { | |
| $this->entities += $entities; | |
| } | |
| } | |
| /** | |
| * Invokes a hook on behalf of the entity. | |
| * | |
| * @param string $hook | |
| * One of 'presave', 'insert', 'update', 'predelete', 'delete', or | |
| * 'revision_delete'. | |
| * @param \Drupal\Core\Entity\EntityInterface $entity | |
| * The entity object. | |
| */ | |
| protected function invokeHook($hook, EntityInterface $entity) { | |
| // Invoke the hook. | |
| $this->moduleHandler()->invokeAll($this->entityTypeId . '_' . $hook, array($entity)); | |
| // Invoke the respective entity-level hook. | |
| $this->moduleHandler()->invokeAll('entity_' . $hook, array($entity)); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function create(array $values = array()) { | |
| $entity_class = $this->entityClass; | |
| $entity_class::preCreate($this, $values); | |
| // Assign a new UUID if there is none yet. | |
| if ($this->uuidKey && $this->uuidService && !isset($values[$this->uuidKey])) { | |
| $values[$this->uuidKey] = $this->uuidService->generate(); | |
| } | |
| $entity = $this->doCreate($values); | |
| $entity->enforceIsNew(); | |
| $entity->postCreate($this); | |
| // Modules might need to add or change the data initially held by the new | |
| // entity object, for instance to fill-in default values. | |
| $this->invokeHook('create', $entity); | |
| return $entity; | |
| } | |
| /** | |
| * Performs storage-specific creation of entities. | |
| * | |
| * @param array $values | |
| * An array of values to set, keyed by property name. | |
| * | |
| * @return \Drupal\Core\Entity\EntityInterface | |
| */ | |
| protected function doCreate(array $values) { | |
| return new $this->entityClass($values, $this->entityTypeId); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function load($id) { | |
| $entities = $this->loadMultiple(array($id)); | |
| return isset($entities[$id]) ? $entities[$id] : NULL; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function loadMultiple(array $ids = NULL) { | |
| $entities = array(); | |
| // Create a new variable which is either a prepared version of the $ids | |
| // array for later comparison with the entity cache, or FALSE if no $ids | |
| // were passed. The $ids array is reduced as items are loaded from cache, | |
| // and we need to know if it's empty for this reason to avoid querying the | |
| // database when all requested entities are loaded from cache. | |
| $passed_ids = !empty($ids) ? array_flip($ids) : FALSE; | |
| // Try to load entities from the static cache, if the entity type supports | |
| // static caching. | |
| if ($this->entityType->isStaticallyCacheable() && $ids) { | |
| $entities += $this->getFromStaticCache($ids); | |
| // If any entities were loaded, remove them from the ids still to load. | |
| if ($passed_ids) { | |
| $ids = array_keys(array_diff_key($passed_ids, $entities)); | |
| } | |
| } | |
| // Load any remaining entities from the database. This is the case if $ids | |
| // is set to NULL (so we load all entities) or if there are any ids left to | |
| // load. | |
| if ($ids === NULL || $ids) { | |
| $queried_entities = $this->doLoadMultiple($ids); | |
| } | |
| // Pass all entities loaded from the database through $this->postLoad(), | |
| // which attaches fields (if supported by the entity type) and calls the | |
| // entity type specific load callback, for example hook_node_load(). | |
| if (!empty($queried_entities)) { | |
| $this->postLoad($queried_entities); | |
| $entities += $queried_entities; | |
| } | |
| if ($this->entityType->isStaticallyCacheable()) { | |
| // Add entities to the cache. | |
| if (!empty($queried_entities)) { | |
| $this->setStaticCache($queried_entities); | |
| } | |
| } | |
| // Ensure that the returned array is ordered the same as the original | |
| // $ids array if this was passed in and remove any invalid ids. | |
| if ($passed_ids) { | |
| // Remove any invalid ids from the array. | |
| $passed_ids = array_intersect_key($passed_ids, $entities); | |
| foreach ($entities as $entity) { | |
| $passed_ids[$entity->id()] = $entity; | |
| } | |
| $entities = $passed_ids; | |
| } | |
| return $entities; | |
| } | |
| /** | |
| * Performs storage-specific loading of entities. | |
| * | |
| * Override this method to add custom functionality directly after loading. | |
| * This is always called, while self::postLoad() is only called when there are | |
| * actual results. | |
| * | |
| * @param array|null $ids | |
| * (optional) An array of entity IDs, or NULL to load all entities. | |
| * | |
| * @return \Drupal\Core\Entity\EntityInterface[] | |
| * Associative array of entities, keyed on the entity ID. | |
| */ | |
| abstract protected function doLoadMultiple(array $ids = NULL); | |
| /** | |
| * Attaches data to entities upon loading. | |
| * | |
| * @param array $entities | |
| * Associative array of query results, keyed on the entity ID. | |
| */ | |
| protected function postLoad(array &$entities) { | |
| $entity_class = $this->entityClass; | |
| $entity_class::postLoad($this, $entities); | |
| // Call hook_entity_load(). | |
| foreach ($this->moduleHandler()->getImplementations('entity_load') as $module) { | |
| $function = $module . '_entity_load'; | |
| $function($entities, $this->entityTypeId); | |
| } | |
| // Call hook_TYPE_load(). | |
| foreach ($this->moduleHandler()->getImplementations($this->entityTypeId . '_load') as $module) { | |
| $function = $module . '_' . $this->entityTypeId . '_load'; | |
| $function($entities); | |
| } | |
| } | |
| /** | |
| * Maps from storage records to entity objects. | |
| * | |
| * @param array $records | |
| * Associative array of query results, keyed on the entity ID. | |
| * | |
| * @return \Drupal\Core\Entity\EntityInterface[] | |
| * An array of entity objects implementing the EntityInterface. | |
| */ | |
| protected function mapFromStorageRecords(array $records) { | |
| $entities = array(); | |
| foreach ($records as $record) { | |
| $entity = new $this->entityClass($record, $this->entityTypeId); | |
| $entities[$entity->id()] = $entity; | |
| } | |
| return $entities; | |
| } | |
| /** | |
| * Determines if this entity already exists in storage. | |
| * | |
| * @param int|string $id | |
| * The original entity ID. | |
| * @param \Drupal\Core\Entity\EntityInterface $entity | |
| * The entity being saved. | |
| * | |
| * @return bool | |
| */ | |
| abstract protected function has($id, EntityInterface $entity); | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function delete(array $entities) { | |
| if (!$entities) { | |
| // If no entities were passed, do nothing. | |
| return; | |
| } | |
| // Ensure that the entities are keyed by ID. | |
| $keyed_entities = []; | |
| foreach ($entities as $entity) { | |
| $keyed_entities[$entity->id()] = $entity; | |
| } | |
| // Allow code to run before deleting. | |
| $entity_class = $this->entityClass; | |
| $entity_class::preDelete($this, $keyed_entities); | |
| foreach ($keyed_entities as $entity) { | |
| $this->invokeHook('predelete', $entity); | |
| } | |
| // Perform the delete and reset the static cache for the deleted entities. | |
| $this->doDelete($keyed_entities); | |
| $this->resetCache(array_keys($keyed_entities)); | |
| // Allow code to run after deleting. | |
| $entity_class::postDelete($this, $keyed_entities); | |
| foreach ($keyed_entities as $entity) { | |
| $this->invokeHook('delete', $entity); | |
| } | |
| } | |
| /** | |
| * Performs storage-specific entity deletion. | |
| * | |
| * @param \Drupal\Core\Entity\EntityInterface[] $entities | |
| * An array of entity objects to delete. | |
| */ | |
| abstract protected function doDelete($entities); | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function save(EntityInterface $entity) { | |
| // Track if this entity is new. | |
| $is_new = $entity->isNew(); | |
| // Execute presave logic and invoke the related hooks. | |
| $id = $this->doPreSave($entity); | |
| // Perform the save and reset the static cache for the changed entity. | |
| $return = $this->doSave($id, $entity); | |
| // Execute post save logic and invoke the related hooks. | |
| $this->doPostSave($entity, !$is_new); | |
| return $return; | |
| } | |
| /** | |
| * Performs presave entity processing. | |
| * | |
| * @param \Drupal\Core\Entity\EntityInterface $entity | |
| * The saved entity. | |
| * | |
| * @return int|string | |
| * The processed entity identifier. | |
| * | |
| * @throws \Drupal\Core\Entity\EntityStorageException | |
| * If the entity identifier is invalid. | |
| */ | |
| protected function doPreSave(EntityInterface $entity) { | |
| $id = $entity->id(); | |
| // Track the original ID. | |
| if ($entity->getOriginalId() !== NULL) { | |
| $id = $entity->getOriginalId(); | |
| } | |
| // Track if this entity exists already. | |
| $id_exists = $this->has($id, $entity); | |
| // A new entity should not already exist. | |
| if ($id_exists && $entity->isNew()) { | |
| throw new EntityStorageException("'{$this->entityTypeId}' entity with ID '$id' already exists."); | |
| } | |
| // Load the original entity, if any. | |
| if ($id_exists && !isset($entity->original)) { | |
| $entity->original = $this->loadUnchanged($id); | |
| } | |
| // Allow code to run before saving. | |
| $entity->preSave($this); | |
| $this->invokeHook('presave', $entity); | |
| return $id; | |
| } | |
| /** | |
| * Performs storage-specific saving of the entity. | |
| * | |
| * @param int|string $id | |
| * The original entity ID. | |
| * @param \Drupal\Core\Entity\EntityInterface $entity | |
| * The entity to save. | |
| * | |
| * @return bool|int | |
| * If the record insert or update failed, returns FALSE. If it succeeded, | |
| * returns SAVED_NEW or SAVED_UPDATED, depending on the operation performed. | |
| */ | |
| abstract protected function doSave($id, EntityInterface $entity); | |
| /** | |
| * Performs post save entity processing. | |
| * | |
| * @param \Drupal\Core\Entity\EntityInterface $entity | |
| * The saved entity. | |
| * @param bool $update | |
| * Specifies whether the entity is being updated or created. | |
| */ | |
| protected function doPostSave(EntityInterface $entity, $update) { | |
| $this->resetCache(array($entity->id())); | |
| // The entity is no longer new. | |
| $entity->enforceIsNew(FALSE); | |
| // Allow code to run after saving. | |
| $entity->postSave($this, $update); | |
| $this->invokeHook($update ? 'update' : 'insert', $entity); | |
| // After saving, this is now the "original entity", and subsequent saves | |
| // will be updates instead of inserts, and updates must always be able to | |
| // correctly identify the original entity. | |
| $entity->setOriginalId($entity->id()); | |
| unset($entity->original); | |
| } | |
| /** | |
| * Builds an entity query. | |
| * | |
| * @param \Drupal\Core\Entity\Query\QueryInterface $entity_query | |
| * EntityQuery instance. | |
| * @param array $values | |
| * An associative array of properties of the entity, where the keys are the | |
| * property names and the values are the values those properties must have. | |
| */ | |
| protected function buildPropertyQuery(QueryInterface $entity_query, array $values) { | |
| foreach ($values as $name => $value) { | |
| // Cast scalars to array so we can consistently use an IN condition. | |
| $entity_query->condition($name, (array) $value, 'IN'); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function loadByProperties(array $values = array()) { | |
| // Build a query to fetch the entity IDs. | |
| $entity_query = $this->getQuery(); | |
| $this->buildPropertyQuery($entity_query, $values); | |
| $result = $entity_query->execute(); | |
| return $result ? $this->loadMultiple($result) : array(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getQuery($conjunction = 'AND') { | |
| // Access the service directly rather than entity.query factory so the | |
| // storage's current entity type is used. | |
| return \Drupal::service($this->getQueryServiceName())->get($this->entityType, $conjunction); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getAggregateQuery($conjunction = 'AND') { | |
| // Access the service directly rather than entity.query factory so the | |
| // storage's current entity type is used. | |
| return \Drupal::service($this->getQueryServiceName())->getAggregate($this->entityType, $conjunction); | |
| } | |
| /** | |
| * Gets the name of the service for the query for this entity storage. | |
| * | |
| * @return string | |
| * The name of the service for the query for this entity storage. | |
| */ | |
| abstract protected function getQueryServiceName(); | |
| } |