diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index b30d9ec..d84a995 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -117,13 +117,9 @@ public function loadMultiple(array $ids = NULL) { // 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. + // static caching. This will remove ID's that were loaded from $ids. if ($this->cache && $ids) { $entities += $this->cacheGet($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 diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php index f303f79..ef99943 100644 --- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php +++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php @@ -109,17 +109,24 @@ public function resetCache(array $ids = NULL) { /** * Gets entities from the static cache. * - * @param $ids - * If not empty, return entities that match these IDs. + * @param array &$ids + * If not empty, return entities that match these IDs. ID's that were found + * will be removed from the list. * - * @return + * @return \Drupal\Core\Entity\EntityInterface[] * Array of entities from the entity cache. */ - protected function cacheGet($ids) { + protected function cacheGet(&$ids) { $entities = array(); // Load any available entities from the internal cache. if ($this->cache && !empty($this->entityCache)) { - $entities += array_intersect_key($this->entityCache, array_flip($ids)); + foreach ($ids as $index => $id) { + if (isset($this->entityCache[$id])) { + $entities[$id] = $this->entityCache[$id]; + // Remove the ID from the list + unset($ids[$index]); + } + } } return $entities; } @@ -127,7 +134,7 @@ protected function cacheGet($ids) { /** * Stores entities in the static entity cache. * - * @param $entities + * @param \Drupal\Core\Entity\EntityInterface[] $entities * Entities to store in the cache. */ protected function cacheSet($entities) { diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php index e5ec6f3..938e216 100644 --- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php @@ -12,7 +12,6 @@ use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Field\PrepareCacheInterface; use Drupal\Core\Language\Language; -use Drupal\Component\Utility\NestedArray; use Drupal\field\FieldInfo; use Drupal\field\FieldUpdateForbiddenException; use Drupal\field\FieldInterface; @@ -222,14 +221,10 @@ public function loadMultiple(array $ids = NULL) { // 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. + // Try to load entities from the cache, if the entity type supports + // static caching. This will remove ID's that were loaded from $ids. if ($this->cache && $ids) { $entities += $this->cacheGet($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 @@ -833,7 +828,7 @@ public function getQueryServiceName() { * Loads all fields for each entity object in a group of a single entity type. * The loaded field values are added directly to the entity objects. * - * @param array $entities + * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities * An array of entities keyed by entity ID. * @param int $age * EntityStorageControllerInterface::FIELD_LOAD_CURRENT to load the most @@ -1014,10 +1009,6 @@ protected function deleteFieldItems(EntityInterface $entity) { /** * Deletes values of configurable fields for a single revision of an entity. * - * This method is a wrapper that handles the field data cache. Subclasses - * need to implement the doDeleteFieldItemsRevision() method with the actual - * storage logic. - * * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. It must have a revision ID. */ @@ -1500,29 +1491,39 @@ static public function _fieldColumnName(FieldInterface $field, $column) { /** * {@inheritdoc} */ - protected function cacheGet($ids) { + protected function cacheGet(&$ids) { + // The parent implementation provides the static cache, so first attempt + // to load entities from it. $entities = parent::cacheGet($ids); - if ($this->cache) { + // @todo: Rename field_cache? + if ($this->entityInfo['field_cache'] && $ids) { + // Now attempt to load the remaining ID's (those that were loaded have been + // removed from $ids already) from the persistent cache. // Build the list of cache entries to retrieve. $cids = array(); foreach ($ids as $id) { - if (!isset($entities[$id])) { - $cids[] = "entity:{$this->entityType}:$id"; - } + $cids[] = $this->buildCacheId($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. + if ($cache = $this->cacheBackend->getMultiple($cids)) { + // Create entity objects based on the loaded values from the cache. + // Put them in a temporary variable so that we can call the load hooks + // for those. + // @todo: Avoid this, which requires that all cached data must + // be in defined fields or find a way to extract non-defined values + // too. $entities_from_cache = array(); - foreach ($ids as $id) { - $cid = "entity:{$this->entityType}:$id"; + foreach ($ids as $index => $id) { + $cid = $this->buildCacheId($id); if (isset($cache[$cid])) { $values = $cache[$cid]->data['values']; $translations = $cache[$cid]->data['translations']; $bundle = $this->bundleKey ? $cache[$cid]->data['bundle'] : FALSE; $entities_from_cache[$id] = new $this->entityClass($values, $this->entityType, $bundle, $translations); - // Already put the loaded entity into the cache. + // Already put the loaded entity into the cache. This will prevent + // cacheSet() from unnecessarily writing them back into the cache + // backend. Do not call that method directly to prevent that. $this->entityCache[$id] = $entities_from_cache[$id]; + unset($ids[$index]); } } @@ -1533,17 +1534,16 @@ protected function cacheGet($ids) { $entities[$id] = $entity; } } - } - return $entities; } + return $entities; } /** * {@inheritdoc} */ protected function cacheSet($entities) { - if ($this->cache) { + if ($this->entityInfo['field_cache']) { foreach ($entities as $id => $entity) { // Skip entities that are already in the static cache. if (isset($this->entityCache[$id])) { @@ -1556,11 +1556,12 @@ protected function cacheSet($entities) { 'translations' => array_keys($entity->getTranslationLanguages()), 'values' => array(), ); + $default_langcode = $entity->getUntranslated()->language()->id; foreach ($entity->getTranslationLanguages() as $langcode => $language) { $translation = $entity->getTranslation($langcode); // Make sure the default language is valid. - if ($entity->getUntranslated()->language()->id == $langcode) { + if ($default_langcode == $langcode) { $langcode = Language::LANGCODE_DEFAULT; } @@ -1580,8 +1581,7 @@ protected function cacheSet($entities) { } } } - $cid = "entity:{$this->entityType}:$id"; - $this->cacheBackend->set($cid, $data); + $this->cacheBackend->set($this->buildCacheId($id), $data, CacheBackendInterface::CACHE_PERMANENT, array($this->entityType . '_values' => TRUE)); } } parent::cacheSet($entities); @@ -1592,15 +1592,17 @@ protected function cacheSet($entities) { */ public function resetCache(array $ids = NULL) { parent::resetCache($ids); - if ($ids) { - $cids = array(); - foreach ($ids as $id) { - $cids[] = "entity:{$this->entityType}:$id"; + if ($this->entityInfo['field_cache']) { + if ($ids) { + $cids = array(); + foreach ($ids as $id) { + $cids[] = $this->buildCacheId($id); + } + $this->cacheBackend->deleteMultiple($cids); + } + else { + $this->cacheBackend->deleteTags(array($this->entityType . '_values' => TRUE)); } - $this->cacheBackend->deleteMultiple($cids); - } - else { - $this->cacheBackend->deleteAll(); } } @@ -1627,4 +1629,17 @@ protected function invokeLoadHook($entities) { } } + /** + * Returns 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->entityType}:$id"; + } + }