diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index 07cf115..92586bc 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -7,11 +7,12 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; +use Drupal\Core\Entity\Field\PrepareCacheInterface; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Language\Language; use Drupal\Component\Utility\NestedArray; -use Drupal\Component\Uuid\Uuid; use Drupal\field\FieldInfo; use Drupal\field\FieldUpdateForbiddenException; use Drupal\field\FieldInterface; @@ -83,6 +84,13 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle protected $fieldInfo; /** + * Cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cacheBackend; + + /** * The entity bundle key. * * @var string|bool @@ -104,7 +112,8 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_type, $entity_info, $container->get('database'), - $container->get('field.info') + $container->get('field.info'), + $container->get('cache.entity') ); } @@ -119,12 +128,15 @@ public static function createInstance(ContainerInterface $container, $entity_typ * The database connection to be used. * @param \Drupal\field\FieldInfo $field_info * The field info service. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. */ - public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) { + public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, CacheBackendInterface $cache) { parent::__construct($entity_type, $entity_info); $this->database = $database; $this->fieldInfo = $field_info; + $this->cacheBackend = $cache; $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE; $this->entityClass = $this->entityInfo['class']; @@ -1467,4 +1479,102 @@ static public function _fieldColumnName(FieldInterface $field, $column) { return in_array($column, Field::getReservedColumns()) ? $column : $field->getFieldName() . '_' . $column; } + + /** + * {@inheritdoc} + */ + protected function cacheGet($ids) { + $entities = parent::cacheGet($ids); + if ($this->cache) { + // Build the list of cache entries to retrieve. + $cids = array(); + foreach ($ids as $id) { + if (!isset($entities[$id])) { + $cids[] = "entity:{$this->entityType}:$id"; + } + } + if ($cids && $cache = $this->cacheBackend->getMultiple($cids)) { + // Put the cached field values back into the entities and remove them from + // the list of entities to query. + foreach ($ids as $id) { + $cid = "entity:{$this->entityType}:$id"; + if (isset($cache[$cid])) { + $values = $cache[$cid]->data['values']; + $translations = $cache[$cid]->data['translations']; + $bundle = $this->bundleKey ? $cache[$cid]->data['bundle'] : FALSE; + $entities[$id] = new $this->entityClass($values, $this->entityType, $bundle, $translations); + // Already put the loaded entity into the cache. + $this->entityCache[$id] = $entities[$id]; + } + } + } + return $entities; + } + } + + /** + * {@inheritdoc} + */ + protected function cacheSet($entities) { + if ($this->cache) { + foreach ($entities as $id => $entity) { + // Skip entities that are already in the static cache. + if (isset($this->entityCache[$id])) { + continue; + } + + $data = array( + 'id' => $entity->id(), + 'bundle' => $entity->bundle(), + 'translations' => array_keys($entity->getTranslationLanguages()), + 'values' => array(), + ); + foreach ($entity->getTranslationLanguages() as $langcode => $language) { + $translation = $entity->getTranslation($langcode); + + // Make sure the default language is valid. + if ($entity->getUntranslated()->language()->id == $langcode) { + $langcode = Language::LANGCODE_DEFAULT; + } + + foreach ($translation as $field_name => $items) { + if (!$items->isEmpty()) { + foreach ($items as $delta => $item) { + // If the field item needs to be prepare the cache data, call + // the corresponding method, otherwise use the values as cache + // data. + if ($item instanceof PrepareCacheInterface) { + $data['values'][$field_name][$langcode][$delta] = $item->getCacheData(); + } + else { + $data['values'][$field_name][$langcode][$delta] = $item->getValue(); + } + } + } + } + } + $cid = "entity:{$this->entityType}:$id"; + $this->cacheBackend->set($cid, $data); + } + } + parent::cacheSet($entities); + } + + /** + * {@inheritdoc} + */ + public function resetCache(array $ids = NULL) { + parent::resetCache($ids); + if ($ids) { + $cids = array(); + foreach ($ids as $id) { + $cids[] = "entity:{$this->entityType}:$id"; + } + $this->cacheBackend->deleteMultiple($cids); + } + else { + $this->cacheBackend->deleteAll(); + } + } + } diff --git a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php index f16d863..700cdcd 100644 --- a/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php +++ b/core/lib/Drupal/Core/Entity/FieldableEntityStorageControllerBase.php @@ -34,80 +34,12 @@ * indicated by each entity. */ protected function loadFieldItems(array $entities, $age) { - if (empty($entities)) { + if (empty($entities) || empty($this->entityInfo['fieldable'])) { return; } - - // Only the most current revision of non-deleted fields for cacheable entity - // types can be cached. - $load_current = $age == static::FIELD_LOAD_CURRENT; - $info = entity_get_info($this->entityType); - $use_cache = $load_current && $info['field_cache']; - - // Assume all entities will need to be queried. Entities found in the cache - // will be removed from the list. - $queried_entities = $entities; - - // Fetch available entities from cache, if applicable. - if ($use_cache) { - // Build the list of cache entries to retrieve. - $cids = array(); - foreach ($entities as $id => $entity) { - $cids[] = "field:{$this->entityType}:$id"; - } - $cache = cache('field')->getMultiple($cids); - // Put the cached field values back into the entities and remove them from - // the list of entities to query. - foreach ($entities as $id => $entity) { - $cid = "field:{$this->entityType}:$id"; - if (isset($cache[$cid])) { - unset($queried_entities[$id]); - foreach ($cache[$cid]->data as $langcode => $values) { - $translation = $entity->getTranslation($langcode); - // We do not need to worry about field translatability here, the - // translation object will manage that automatically. - foreach ($values as $field_name => $items) { - $translation->$field_name = $items; - } - } - } - } - } - - // Fetch other entities from their storage location. - if ($queried_entities) { - // Let the storage controller actually load the values. - $this->doLoadFieldItems($queried_entities, $age); - - // Build cache data. - // @todo: Improve this logic to avoid instantiating field objects once - // the field logic is improved to not do that anyway. - if ($use_cache) { - foreach ($queried_entities as $id => $entity) { - $data = array(); - foreach ($entity->getTranslationLanguages() as $langcode => $language) { - $translation = $entity->getTranslation($langcode); - foreach ($translation as $field_name => $items) { - if ($items instanceof ConfigFieldItemListInterface && !$items->isEmpty()) { - foreach ($items as $delta => $item) { - // If the field item needs to prepare the cache data, call the - // corresponding method, otherwise use the values as cache - // data. - if ($item instanceof PrepareCacheInterface) { - $data[$langcode][$field_name][$delta] = $item->getCacheData(); - } - else { - $data[$langcode][$field_name][$delta] = $item->getValue(); - } - } - } - } - } - $cid = "field:{$this->entityType}:$id"; - cache('field')->set($cid, $data); - } - } - } + // @todo: Drop these wrappers. + // Let the storage controller actually load the values. + $this->doLoadFieldItems($entities, $age); } /** @@ -123,14 +55,8 @@ protected function loadFieldItems(array $entities, $age) { * TRUE if the entity is being updated, FALSE if it is being inserted. */ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { + // @todo: Drop these wrappers. $this->doSaveFieldItems($entity, $update); - - if ($update) { - $entity_info = $entity->entityInfo(); - if ($entity_info['field_cache']) { - cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id()); - } - } } /** @@ -144,12 +70,8 @@ protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { * The entity. */ protected function deleteFieldItems(EntityInterface $entity) { + // @todo: Drop these wrappers. $this->doDeleteFieldItems($entity); - - $entity_info = $entity->entityInfo(); - if ($entity_info['field_cache']) { - cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id()); - } } /** diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php index 48e8206..bb433b0 100644 --- a/core/modules/user/lib/Drupal/user/UserStorageController.php +++ b/core/modules/user/lib/Drupal/user/UserStorageController.php @@ -8,6 +8,7 @@ namespace Drupal\user; use Drupal\Component\Uuid\UuidInterface; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Password\PasswordInterface; use Drupal\Core\Database\Connection; @@ -49,15 +50,15 @@ class UserStorageController extends FieldableDatabaseStorageController implement * The database connection to be used. * @param \Drupal\field\FieldInfo $field_info * The field info service. - * @param \Drupal\Component\Uuid\UuidInterface $uuid_service - * The UUID Service. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use.. * @param \Drupal\Core\Password\PasswordInterface $password * The password hashing service. * @param \Drupal\user\UserDataInterface $user_data * The user data service. */ - public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $entity_info, $database, $field_info, $uuid_service); + public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, CacheBackendInterface $cache, PasswordInterface $password, UserDataInterface $user_data) { + parent::__construct($entity_type, $entity_info, $database, $field_info, $cache); $this->password = $password; $this->userData = $user_data; @@ -72,7 +73,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ $entity_info, $container->get('database'), $container->get('field.info'), - $container->get('uuid'), + $container->get('cache.entity'), $container->get('password'), $container->get('user.data') );