Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
26.92% |
7 / 26 |
CRAP | |
20.65% |
38 / 184 |
ContentEntityStorageBase | |
0.00% |
0 / 1 |
|
51.28% |
20 / 39 |
5299.63 | |
20.65% |
38 / 184 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
createInstance | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
hasData | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
doCreate | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 8 |
|||
initFieldValues | |
0.00% |
0 / 1 |
56 | |
0.00% |
0 / 11 |
|||
createTranslation | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 6 |
|||
anonymous function | |
100.00% |
1 / 1 |
2 | |
100.00% |
2 / 2 |
|||
onFieldStorageDefinitionCreate | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
onFieldStorageDefinitionUpdate | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
onFieldStorageDefinitionDelete | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
onFieldDefinitionCreate | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
onFieldDefinitionUpdate | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
onFieldDefinitionDelete | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
purgeFieldData | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
readFieldItemsToPurge | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
purgeFieldItems | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
finalizePurge | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
loadRevision | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
doLoadRevisionFieldItems | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
doSave | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
doSaveFieldItems | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
doPreSave | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
doPostSave | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 6 |
|||
doDelete | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
doDeleteFieldItems | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
deleteRevision | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 7 |
|||
doDeleteRevisionFieldItems | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
invokeTranslationHooks | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 9 |
|||
invokeStorageLoadHook | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 8 |
|||
invokeHook | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 11 |
|||
invokeFieldMethod | |
0.00% |
0 / 1 |
90 | |
0.00% |
0 / 16 |
|||
invokeFieldPostSave | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
|||
hasFieldValueChanged | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 10 |
|||
populateAffectedRevisionTranslations | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 8 |
|||
cleanIds | |
100.00% |
1 / 1 |
3 | |
100.00% |
3 / 3 |
|||
getFromPersistentCache | |
100.00% |
1 / 1 |
7 | |
100.00% |
14 / 14 |
|||
setPersistentCache | |
100.00% |
1 / 1 |
3 | |
100.00% |
7 / 7 |
|||
resetCache | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 11 |
|||
buildCacheId | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Entity\ContentEntityStorageBase. | |
*/ | |
namespace Drupal\Core\Entity; | |
use Drupal\Core\Cache\Cache; | |
use Drupal\Core\Cache\CacheBackendInterface; | |
use Drupal\Core\Field\FieldDefinitionInterface; | |
use Drupal\Core\Field\FieldStorageDefinitionInterface; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
/** | |
* Base class for content entity storage handlers. | |
*/ | |
abstract class ContentEntityStorageBase extends EntityStorageBase implements ContentEntityStorageInterface, DynamicallyFieldableEntityStorageInterface { | |
/** | |
* The entity bundle key. | |
* | |
* @var string|bool | |
*/ | |
protected $bundleKey = FALSE; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
*/ | |
protected $entityManager; | |
/** | |
* Cache backend. | |
* | |
* @var \Drupal\Core\Cache\CacheBackendInterface | |
*/ | |
protected $cacheBackend; | |
/** | |
* Constructs a ContentEntityStorageBase object. | |
* | |
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type | |
* The entity type definition. | |
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager | |
* The entity manager. | |
* @param \Drupal\Core\Cache\CacheBackendInterface $cache | |
* The cache backend to be used. | |
*/ | |
public function __construct(EntityTypeInterface $entity_type, EntityManagerInterface $entity_manager, CacheBackendInterface $cache) { | |
parent::__construct($entity_type); | |
$this->bundleKey = $this->entityType->getKey('bundle'); | |
$this->entityManager = $entity_manager; | |
$this->cacheBackend = $cache; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { | |
return new static( | |
$entity_type, | |
$container->get('entity.manager'), | |
$container->get('cache.entity') | |
); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hasData() { | |
return (bool) $this->getQuery() | |
->accessCheck(FALSE) | |
->range(0, 1) | |
->execute(); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function doCreate(array $values) { | |
// We have to determine the bundle first. | |
$bundle = FALSE; | |
if ($this->bundleKey) { | |
if (!isset($values[$this->bundleKey])) { | |
throw new EntityStorageException('Missing bundle for entity type ' . $this->entityTypeId); | |
} | |
$bundle = $values[$this->bundleKey]; | |
} | |
$entity = new $this->entityClass(array(), $this->entityTypeId, $bundle); | |
$this->initFieldValues($entity, $values); | |
return $entity; | |
} | |
/** | |
* Initializes field values. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* An entity object. | |
* @param array $values | |
* (optional) An associative array of initial field values keyed by field | |
* name. If none is provided default values will be applied. | |
* @param array $field_names | |
* (optional) An associative array of field names to be initialized. If none | |
* is provided all fields will be initialized. | |
*/ | |
protected function initFieldValues(ContentEntityInterface $entity, array $values = [], array $field_names = []) { | |
// Populate field values. | |
foreach ($entity as $name => $field) { | |
if (!$field_names || isset($field_names[$name])) { | |
if (isset($values[$name])) { | |
$entity->$name = $values[$name]; | |
} | |
elseif (!array_key_exists($name, $values)) { | |
$entity->get($name)->applyDefaultValue(); | |
} | |
} | |
unset($values[$name]); | |
} | |
// Set any passed values for non-defined fields also. | |
foreach ($values as $name => $value) { | |
$entity->$name = $value; | |
} | |
// Make sure modules can alter field initial values. | |
$this->invokeHook('field_values_init', $entity); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []) { | |
$translation = $entity->getTranslation($langcode); | |
$definitions = array_filter($translation->getFieldDefinitions(), function(FieldDefinitionInterface $definition) { return $definition->isTranslatable(); }); | |
$field_names = array_map(function(FieldDefinitionInterface $definition) { return $definition->getName(); }, $definitions); | |
$values[$this->langcodeKey] = $langcode; | |
$values[$this->getEntityType()->getKey('default_langcode')] = FALSE; | |
$this->initFieldValues($translation, $values, $field_names); | |
$this->invokeHook('translation_create', $translation); | |
return $translation; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldDefinitionCreate(FieldDefinitionInterface $field_definition) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldDefinitionUpdate(FieldDefinitionInterface $field_definition, FieldDefinitionInterface $original) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definition) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function purgeFieldData(FieldDefinitionInterface $field_definition, $batch_size) { | |
$items_by_entity = $this->readFieldItemsToPurge($field_definition, $batch_size); | |
foreach ($items_by_entity as $items) { | |
$items->delete(); | |
$this->purgeFieldItems($items->getEntity(), $field_definition); | |
} | |
return count($items_by_entity); | |
} | |
/** | |
* Reads values to be purged for a single field. | |
* | |
* This method is called during field data purge, on fields for which | |
* onFieldDefinitionDelete() has previously run. | |
* | |
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition | |
* The field definition. | |
* @param $batch_size | |
* The maximum number of field data records to purge before returning. | |
* | |
* @return \Drupal\Core\Field\FieldItemListInterface[] | |
* An array of field item lists, keyed by entity revision id. | |
*/ | |
abstract protected function readFieldItemsToPurge(FieldDefinitionInterface $field_definition, $batch_size); | |
/** | |
* Removes field items from storage per entity during purge. | |
* | |
* @param ContentEntityInterface $entity | |
* The entity revision, whose values are being purged. | |
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition | |
* The field whose values are bing purged. | |
*/ | |
abstract protected function purgeFieldItems(ContentEntityInterface $entity, FieldDefinitionInterface $field_definition); | |
/** | |
* {@inheritdoc} | |
*/ | |
public function finalizePurge(FieldStorageDefinitionInterface $storage_definition) { } | |
/** | |
* {@inheritdoc} | |
*/ | |
public function loadRevision($revision_id) { | |
$revision = $this->doLoadRevisionFieldItems($revision_id); | |
if ($revision) { | |
$entities = [$revision->id() => $revision]; | |
$this->invokeStorageLoadHook($entities); | |
$this->postLoad($entities); | |
} | |
return $revision; | |
} | |
/** | |
* Actually loads revision field item values from the storage. | |
* | |
* @param int|string $revision_id | |
* The revision identifier. | |
* | |
* @return \Drupal\Core\Entity\EntityInterface|null | |
* The specified entity revision or NULL if not found. | |
*/ | |
abstract protected function doLoadRevisionFieldItems($revision_id); | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function doSave($id, EntityInterface $entity) { | |
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ | |
if ($entity->isNew()) { | |
// Ensure the entity is still seen as new after assigning it an id, while | |
// storing its data. | |
$entity->enforceIsNew(); | |
if ($this->entityType->isRevisionable()) { | |
$entity->setNewRevision(); | |
} | |
$return = SAVED_NEW; | |
} | |
else { | |
// @todo Consider returning a different value when saving a non-default | |
// entity revision. See https://www.drupal.org/node/2509360. | |
$return = $entity->isDefaultRevision() ? SAVED_UPDATED : FALSE; | |
} | |
$this->populateAffectedRevisionTranslations($entity); | |
$this->doSaveFieldItems($entity); | |
return $return; | |
} | |
/** | |
* Writes entity field values to the storage. | |
* | |
* This method is responsible for allocating entity and revision identifiers | |
* and updating the entity object with their values. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* The entity object. | |
* @param string[] $names | |
* (optional) The name of the fields to be written to the storage. If an | |
* empty value is passed all field values are saved. | |
*/ | |
abstract protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []); | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function doPreSave(EntityInterface $entity) { | |
/** @var \Drupal\Core\Entity\ContentEntityBase $entity */ | |
// Sync the changes made in the fields array to the internal values array. | |
$entity->updateOriginalValues(); | |
return parent::doPreSave($entity); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function doPostSave(EntityInterface $entity, $update) { | |
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ | |
if ($update && $this->entityType->isTranslatable()) { | |
$this->invokeTranslationHooks($entity); | |
} | |
parent::doPostSave($entity, $update); | |
// The revision is stored, it should no longer be marked as new now. | |
if ($this->entityType->isRevisionable()) { | |
$entity->setNewRevision(FALSE); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function doDelete($entities) { | |
/** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ | |
foreach ($entities as $entity) { | |
$this->invokeFieldMethod('delete', $entity); | |
} | |
$this->doDeleteFieldItems($entities); | |
} | |
/** | |
* Deletes entity field values from the storage. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface[] $entities | |
* An array of entity objects to be deleted. | |
*/ | |
abstract protected function doDeleteFieldItems($entities); | |
/** | |
* {@inheritdoc} | |
*/ | |
public function deleteRevision($revision_id) { | |
/** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ | |
if ($revision = $this->loadRevision($revision_id)) { | |
// Prevent deletion if this is the default revision. | |
if ($revision->isDefaultRevision()) { | |
throw new EntityStorageException('Default revision can not be deleted'); | |
} | |
$this->invokeFieldMethod('deleteRevision', $revision); | |
$this->doDeleteRevisionFieldItems($revision); | |
$this->invokeHook('revision_delete', $revision); | |
} | |
} | |
/** | |
* Deletes field values of an entity revision from the storage. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $revision | |
* An entity revision object to be deleted. | |
*/ | |
abstract protected function doDeleteRevisionFieldItems(ContentEntityInterface $revision); | |
/** | |
* Checks translation statuses and invoke the related hooks if needed. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* The entity being saved. | |
*/ | |
protected function invokeTranslationHooks(ContentEntityInterface $entity) { | |
$translations = $entity->getTranslationLanguages(FALSE); | |
$original_translations = $entity->original->getTranslationLanguages(FALSE); | |
$all_translations = array_keys($translations + $original_translations); | |
// Notify modules of translation insertion/deletion. | |
foreach ($all_translations as $langcode) { | |
if (isset($translations[$langcode]) && !isset($original_translations[$langcode])) { | |
$this->invokeHook('translation_insert', $entity->getTranslation($langcode)); | |
} | |
elseif (!isset($translations[$langcode]) && isset($original_translations[$langcode])) { | |
$this->invokeHook('translation_delete', $entity->original->getTranslation($langcode)); | |
} | |
} | |
} | |
/** | |
* Invokes hook_entity_storage_load(). | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface[] $entities | |
* List of entities, keyed on the entity ID. | |
*/ | |
protected function invokeStorageLoadHook(array &$entities) { | |
if (!empty($entities)) { | |
// Call hook_entity_storage_load(). | |
foreach ($this->moduleHandler()->getImplementations('entity_storage_load') as $module) { | |
$function = $module . '_entity_storage_load'; | |
$function($entities, $this->entityTypeId); | |
} | |
// Call hook_TYPE_storage_load(). | |
foreach ($this->moduleHandler()->getImplementations($this->entityTypeId . '_storage_load') as $module) { | |
$function = $module . '_' . $this->entityTypeId . '_storage_load'; | |
$function($entities); | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
protected function invokeHook($hook, EntityInterface $entity) { | |
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ | |
switch ($hook) { | |
case 'presave': | |
$this->invokeFieldMethod('preSave', $entity); | |
break; | |
case 'insert': | |
$this->invokeFieldPostSave($entity, FALSE); | |
break; | |
case 'update': | |
$this->invokeFieldPostSave($entity, TRUE); | |
break; | |
} | |
parent::invokeHook($hook, $entity); | |
} | |
/** | |
* Invokes a method on the Field objects within an entity. | |
* | |
* Any argument passed will be forwarded to the invoked method. | |
* | |
* @param string $method | |
* The name of the method to be invoked. | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* The entity object. | |
* | |
* @return array | |
* A multidimensional associative array of results, keyed by entity | |
* translation language code and field name. | |
*/ | |
protected function invokeFieldMethod($method, ContentEntityInterface $entity) { | |
$result = []; | |
$args = array_slice(func_get_args(), 2); | |
$langcodes = array_keys($entity->getTranslationLanguages()); | |
foreach ($langcodes as $langcode) { | |
$translation = $entity->getTranslation($langcode); | |
// For non translatable fields, there is only one field object instance | |
// across all translations and it has as parent entity the entity in the | |
// default entity translation. Therefore field methods on non translatable | |
// fields should be invoked only on the default entity translation. | |
$fields = $translation->isDefaultTranslation() ? $translation->getFields() : $translation->getTranslatableFields(); | |
foreach ($fields as $name => $items) { | |
// call_user_func_array() is way slower than a direct call so we avoid | |
// using it if have no parameters. | |
$result[$langcode][$name] = $args ? call_user_func_array([$items, $method], $args) : $items->{$method}(); | |
} | |
} | |
// We need to call the delete method for field items of removed | |
// translations. | |
if ($method == 'postSave' && !empty($entity->original)) { | |
$original_langcodes = array_keys($entity->original->getTranslationLanguages()); | |
foreach (array_diff($original_langcodes, $langcodes) as $removed_langcode) { | |
$translation = $entity->original->getTranslation($removed_langcode); | |
$fields = $translation->getTranslatableFields(); | |
foreach ($fields as $name => $items) { | |
$items->delete(); | |
} | |
} | |
} | |
return $result; | |
} | |
/** | |
* Invokes the post save method on the Field objects within an entity. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* The entity object. | |
* @param bool $update | |
* Specifies whether the entity is being updated or created. | |
*/ | |
protected function invokeFieldPostSave(ContentEntityInterface $entity, $update) { | |
// For each entity translation this returns an array of resave flags keyed | |
// by field name, thus we merge them to obtain a list of fields to resave. | |
$resave = []; | |
foreach ($this->invokeFieldMethod('postSave', $entity, $update) as $translation_results) { | |
$resave += array_filter($translation_results); | |
} | |
if ($resave) { | |
$this->doSaveFieldItems($entity, array_keys($resave)); | |
} | |
} | |
/** | |
* Checks whether the field values changed compared to the original entity. | |
* | |
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition | |
* Field definition of field to compare for changes. | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* Entity to check for field changes. | |
* @param \Drupal\Core\Entity\ContentEntityInterface $original | |
* Original entity to compare against. | |
* | |
* @return bool | |
* True if the field value changed from the original entity. | |
*/ | |
protected function hasFieldValueChanged(FieldDefinitionInterface $field_definition, ContentEntityInterface $entity, ContentEntityInterface $original) { | |
$field_name = $field_definition->getName(); | |
$langcodes = array_keys($entity->getTranslationLanguages()); | |
if ($langcodes !== array_keys($original->getTranslationLanguages())) { | |
// If the list of langcodes has changed, we need to save. | |
return TRUE; | |
} | |
foreach ($langcodes as $langcode) { | |
$items = $entity->getTranslation($langcode)->get($field_name)->filterEmptyItems(); | |
$original_items = $original->getTranslation($langcode)->get($field_name)->filterEmptyItems(); | |
// If the field items are not equal, we need to save. | |
if (!$items->equals($original_items)) { | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
* Populates the affected flag for all the revision translations. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface $entity | |
* An entity object being saved. | |
*/ | |
protected function populateAffectedRevisionTranslations(ContentEntityInterface $entity) { | |
if ($this->entityType->isTranslatable() && $this->entityType->isRevisionable()) { | |
$languages = $entity->getTranslationLanguages(); | |
foreach ($languages as $langcode => $language) { | |
$translation = $entity->getTranslation($langcode); | |
// Avoid populating the value if it was already manually set. | |
$affected = $translation->isRevisionTranslationAffected(); | |
if (!isset($affected) && $translation->hasTranslationChanges()) { | |
$translation->setRevisionTranslationAffected(TRUE); | |
} | |
} | |
} | |
} | |
/** | |
* Ensures integer entity IDs are valid. | |
* | |
* The identifier sanitization provided by this method has been introduced | |
* as Drupal used to rely on the database to facilitate this, which worked | |
* correctly with MySQL but led to errors with other DBMS such as PostgreSQL. | |
* | |
* @param array $ids | |
* The entity IDs to verify. | |
* | |
* @return array | |
* The sanitized list of entity IDs. | |
*/ | |
protected function cleanIds(array $ids) { | |
$definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId); | |
$id_definition = $definitions[$this->entityType->getKey('id')]; | |
if ($id_definition->getType() == 'integer') { | |
$ids = array_filter($ids, function ($id) { | |
return is_numeric($id) && $id == (int) $id; | |
}); | |
$ids = array_map('intval', $ids); | |
} | |
return $ids; | |
} | |
/** | |
* Gets entities from the persistent cache backend. | |
* | |
* @param array|null &$ids | |
* If not empty, return entities that match these IDs. IDs that were found | |
* will be removed from the list. | |
* | |
* @return \Drupal\Core\Entity\ContentEntityInterface[] | |
* Array of entities from the persistent cache. | |
*/ | |
protected function getFromPersistentCache(array &$ids = NULL) { | |
if (!$this->entityType->isPersistentlyCacheable() || empty($ids)) { | |
return array(); | |
} | |
$entities = array(); | |
// Build the list of cache entries to retrieve. | |
$cid_map = array(); | |
foreach ($ids as $id) { | |
$cid_map[$id] = $this->buildCacheId($id); | |
} | |
$cids = array_values($cid_map); | |
if ($cache = $this->cacheBackend->getMultiple($cids)) { | |
// Get the entities that were found in the cache. | |
foreach ($ids as $index => $id) { | |
$cid = $cid_map[$id]; | |
if (isset($cache[$cid])) { | |
$entities[$id] = $cache[$cid]->data; | |
unset($ids[$index]); | |
} | |
} | |
} | |
return $entities; | |
} | |
/** | |
* Stores entities in the persistent cache backend. | |
* | |
* @param \Drupal\Core\Entity\ContentEntityInterface[] $entities | |
* Entities to store in the cache. | |
*/ | |
protected function setPersistentCache($entities) { | |
if (!$this->entityType->isPersistentlyCacheable()) { | |
return; | |
} | |
$cache_tags = array( | |
$this->entityTypeId . '_values', | |
'entity_field_info', | |
); | |
foreach ($entities as $id => $entity) { | |
$this->cacheBackend->set($this->buildCacheId($id), $entity, CacheBackendInterface::CACHE_PERMANENT, $cache_tags); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function resetCache(array $ids = NULL) { | |
if ($ids) { | |
$cids = array(); | |
foreach ($ids as $id) { | |
unset($this->entities[$id]); | |
$cids[] = $this->buildCacheId($id); | |
} | |
if ($this->entityType->isPersistentlyCacheable()) { | |
$this->cacheBackend->deleteMultiple($cids); | |
} | |
} | |
else { | |
$this->entities = array(); | |
if ($this->entityType->isPersistentlyCacheable()) { | |
Cache::invalidateTags(array($this->entityTypeId . '_values')); | |
} | |
} | |
} | |
/** | |
* Builds the cache ID for the passed in entity ID. | |
* | |
* @param int $id | |
* Entity ID for which the cache ID should be built. | |
* | |
* @return string | |
* Cache ID that can be passed to the cache backend. | |
*/ | |
protected function buildCacheId($id) { | |
return "values:{$this->entityTypeId}:$id"; | |
} | |
} |