diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 1895cfd..da433dd 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Entity\Plugin\DataType\EntityReference; +use Drupal\Core\Field\PrepareCacheInterface; use Drupal\Core\Entity\TypedData\EntityDataDefinition; use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; @@ -156,7 +157,14 @@ public function __construct(array $values, $entity_type, $bundle = FALSE, $trans foreach ($this->getEntityType()->getKeys() as $key => $field_name) { if (isset($this->values[$field_name])) { if (is_array($this->values[$field_name]) && isset($this->values[$field_name][Language::LANGCODE_DEFAULT])) { - $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT]; + if (is_array($this->values[$field_name][Language::LANGCODE_DEFAULT])) { + if (isset($this->values[$field_name][Language::LANGCODE_DEFAULT][0]['value'])) { + $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT][0]['value']; + } + } + else { + $this->entityKeys[$key] = $this->values[$field_name][Language::LANGCODE_DEFAULT]; + } } } } @@ -1036,6 +1044,45 @@ protected function getEntityKey($key) { /** * {@inheritdoc} */ + public function getCacheData() { + // Start with all the values, so that the data will also include values + // which are not defined as fields. + // @todo: Remove support for this when all entity values are fields. + $data = $this->values; + $default_langcode = $this->getUntranslated()->language()->id; + foreach ($this->getTranslationLanguages() as $langcode => $language) { + $translation = $this->getTranslation($langcode); + // Make sure the default language is valid. + if ($default_langcode == $langcode) { + $langcode = Language::LANGCODE_DEFAULT; + } + foreach ($translation as $field_name => $items) { + if (!$items->isEmpty()) { + // Remove existing values for defined fields, so that their + // structure is standardized. + if (isset($data[$field_name][$langcode])) { + unset($data[$field_name][$langcode]); + } + 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[$field_name][$langcode][$delta] = $item->getCacheData(); + } + else { + $data[$field_name][$langcode][$delta] = $item->getValue(); + } + } + } + } + } + return $data; + } + + /** + * {@inheritdoc} + */ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { return array(); } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index 2214894..9d3a137 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -7,9 +7,11 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\Field\PrepareCacheInterface; use Drupal\Core\Language\Language; use Drupal\field\FieldConfigUpdateForbiddenException; use Drupal\field\FieldConfigInterface; @@ -72,13 +74,21 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase { protected $entityManager; /** + * Cache backend. + * + * @var \Drupal\Core\Cache\CacheBackendInterface + */ + protected $cacheBackend; + + /** * {@inheritdoc} */ public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { return new static( $entity_type, $container->get('database'), - $container->get('entity.manager') + $container->get('entity.manager'), + $container->get('cache.entity') ); } @@ -91,12 +101,15 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * The database connection to be used. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager) { + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache) { parent::__construct($entity_type); $this->database = $database; $this->entityManager = $entity_manager; + $this->cacheBackend = $cache; // Check if the entity type supports UUIDs. $this->uuidKey = $this->entityType->getKey('uuid'); @@ -121,13 +134,167 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa * {@inheritdoc} */ protected function doLoadMultiple(array $ids = NULL) { - // Build and execute the query. - $records = $this - ->buildQuery($ids) - ->execute() - ->fetchAllAssoc($this->idKey); + // Attempt to load entities from the persistent cache. This will remove IDs + // that were loaded from $ids. + $entities_from_cache = $this->getFromPersistentCache($ids); - return $this->mapFromStorageRecords($records); + // Load any remaining entities from the database. + $entities_from_storage = $this->getFromStorage($ids); + $this->setPersistentCache($entities_from_storage); + + return $entities_from_cache + $entities_from_storage; + } + + /** + * Gets entities from the storage. + * + * @param array|null $ids + * If not empty, return entities that match these IDs. Return all entities + * when NULL. + * + * @return \Drupal\Core\Entity\ContentEntityInterface[] + * Array of entities from the storage. + */ + protected function getFromStorage(array $ids = NULL) { + $entities = array(); + + if ($ids === NULL || $ids) { + // Build and execute the query. + $query_result = $this->buildQuery($ids)->execute(); + $records = $query_result->fetchAllAssoc($this->idKey); + + // Map the loaded records into entity objects and according fields. + if ($records) { + $entities = $this->mapFromStorageRecords($records); + + // 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); + } + } + } + + return $entities; + } + + /** + * Gets entities from the persistent cache. + * + * @param array &$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 entity cache. + */ + protected function getFromPersistentCache(&$ids) { + if (!$this->entityType->isPersistentlyCacheable() || empty($ids)) { + return array(); + } + $entities = array(); + // Build the list of cache entries to retrieve. + $cids = array(); + foreach ($ids as $id) { + $cids[] = $this->buildCacheId($id); + } + if ($cache = $this->cacheBackend->getMultiple($cids)) { + // Create entity objects based on the loaded values from the cache. + 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[$id] = new $this->entityClass($values, $this->entityTypeId, $bundle, $translations); + unset($ids[$index]); + } + } + } + return $entities; + } + + /** + * Stores entities in the persistent entity cache. + * + * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities + * Entities to store in the cache. + */ + protected function setPersistentCache($entities) { + if (!$this->entityType->isPersistentlyCacheable()) { + return; + } + + foreach ($entities as $id => $entity) { + $data = array( + 'id' => $entity->id(), + 'bundle' => $entity->bundle(), + 'translations' => array_keys($entity->getTranslationLanguages()), + 'values' => $entity->getCacheData(), + ); + $this->cacheBackend->set($this->buildCacheId($id), $data, CacheBackendInterface::CACHE_PERMANENT, array($this->entityTypeId . '_values' => TRUE, array('entity_field_info' => TRUE))); + } + } + + /** + * Invokes hook_entity_load_uncached(). + * + * @param \Drupal\Core\Entity\ContentEntityInterface[] $entities + * List of entities, keyed on the entity ID. + */ + protected function invokeLoadUncachedHook(array &$entities) { + if (!empty($entities)) { + // Call hook_entity_load_uncached(). + foreach ($this->moduleHandler()->getImplementations('entity_load_uncached') as $module) { + $function = $module . '_entity_load_uncached'; + $function($entities, $this->entityTypeId); + } + // Call hook_TYPE_load_uncached(). + foreach ($this->moduleHandler()->getImplementations($this->entityTypeId . '_load_uncached') as $module) { + $function = $module . '_' . $this->entityTypeId . '_load_uncached'; + $function($entities); + } + } + } + + /** + * {@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()) { + $this->cacheBackend->deleteTags(array($this->entityTypeId . '_values' => TRUE)); + } + } + } + + /** + * 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->entityTypeId}:$id"; } /** @@ -712,9 +879,26 @@ public function getQueryServiceName() { } /** - * {@inheritdoc} + * Loads values of configurable fields for a group of entities. + * + * 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 \Drupal\Core\Entity\ContentEntityInterface[] $entities + * An array of entities keyed by entity ID. */ - protected function doLoadFieldItems($entities, $age) { + protected function loadFieldItems(array $entities) { + if (empty($entities) || !$this->entityType->isFieldable()) { + return; + } + + $age = static::FIELD_LOAD_CURRENT; + foreach ($entities as $entity) { + if (!$entity->isDefaultRevision()) { + $age = static::FIELD_LOAD_REVISION; + break; + } + } $load_current = $age == static::FIELD_LOAD_CURRENT; // Collect entities ids, bundles and languages. @@ -783,9 +967,14 @@ protected function doLoadFieldItems($entities, $age) { } /** - * {@inheritdoc} + * Saves values of configurable fields for an entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. + * @param bool $update + * TRUE if the entity is being updated, FALSE if it is being inserted. */ - protected function doSaveFieldItems(EntityInterface $entity, $update) { + protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { $vid = $entity->getRevisionId(); $id = $entity->id(); $bundle = $entity->bundle(); @@ -871,9 +1060,12 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { } /** - * {@inheritdoc} + * Deletes values of configurable fields for all revisions of an entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. */ - protected function doDeleteFieldItems(EntityInterface $entity) { + protected function deleteFieldItems(EntityInterface $entity) { foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { if (!($instance instanceof FieldInstanceConfigInterface)) { continue; @@ -891,9 +1083,12 @@ protected function doDeleteFieldItems(EntityInterface $entity) { } /** - * {@inheritdoc} + * Deletes values of configurable fields for a single revision of an entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity. It must have a revision ID. */ - protected function doDeleteFieldItemsRevision(EntityInterface $entity) { + protected function deleteFieldItemsRevision(EntityInterface $entity) { $vid = $entity->getRevisionId(); if (isset($vid)) { foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php index 654fc5e..b5f51ce 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity; +use Drupal\Core\Field\PrepareCacheInterface; use Drupal\Core\TypedData\ComplexDataInterface; use Drupal\Core\TypedData\TranslatableInterface; @@ -26,7 +27,7 @@ * @see \Drupal\Core\TypedData\TypedDataManager * @see \Drupal\Core\Field\FieldItemListInterface */ -interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface { +interface ContentEntityInterface extends EntityInterface, RevisionableInterface, TranslatableInterface, ComplexDataInterface, PrepareCacheInterface { /** * Marks the translation identified by the given language code as existing. diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index b75e9bd..16ae251 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -76,206 +76,6 @@ protected function doCreate(array $values) { } /** - * Loads values of configurable fields for a group of entities. - * - * 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. - * - * This method is a wrapper that handles the field data cache. Subclasses - * need to implement the doLoadFieldItems() method with the actual storage - * logic. - * - * @param array $entities - * An array of entities keyed by entity ID. - */ - protected function loadFieldItems(array $entities) { - if (empty($entities)) { - return; - } - - $age = static::FIELD_LOAD_CURRENT; - foreach ($entities as $entity) { - if (!$entity->isDefaultRevision()) { - $age = static::FIELD_LOAD_REVISION; - break; - } - } - - // Only the most current revision of non-deleted fields for cacheable entity - // types can be cached. - $load_current = $age == static::FIELD_LOAD_CURRENT; - $use_cache = $load_current && $this->entityType->isFieldDataCacheable(); - - // 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->entityTypeId}:$id"; - } - $cache = \Drupal::cache('entity')->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->entityTypeId}:$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 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->getFieldDefinition() instanceof FieldInstanceConfigInterface && !$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->entityTypeId}:$id"; - \Drupal::cache('entity')->set($cid, $data, Cache::PERMANENT, array('entity_field_info' => TRUE)); - } - } - } - } - - /** - * Saves values of configurable fields for an entity. - * - * This method is a wrapper that handles the field data cache. Subclasses - * need to implement the doSaveFieldItems() method with the actual storage - * logic. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - * @param bool $update - * TRUE if the entity is being updated, FALSE if it is being inserted. - */ - protected function saveFieldItems(EntityInterface $entity, $update = TRUE) { - $this->doSaveFieldItems($entity, $update); - - if ($update) { - $entity_type = $entity->getEntityType(); - if ($entity_type->isFieldDataCacheable()) { - \Drupal::cache('entity')->delete('field:' . $entity->getEntityTypeId() . ':' . $entity->id()); - } - } - } - - /** - * Deletes values of configurable fields for all revisions of an entity. - * - * This method is a wrapper that handles the field data cache. Subclasses - * need to implement the doDeleteFieldItems() method with the actual storage - * logic. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - */ - protected function deleteFieldItems(EntityInterface $entity) { - $this->doDeleteFieldItems($entity); - - $entity_type = $entity->getEntityType(); - if ($entity_type->isFieldDataCacheable()) { - \Drupal::cache('entity')->delete('field:' . $entity->getEntityTypeId() . ':' . $entity->id()); - } - } - - /** - * 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 attribute. - */ - protected function deleteFieldItemsRevision(EntityInterface $entity) { - $this->doDeleteFieldItemsRevision($entity); - } - - /** - * Loads values of configurable fields for a group of entities. - * - * This is the method that holds the actual storage logic. - * - * @param array $entities - * An array of entities keyed by entity ID. - * @param int $age - * EntityStorageInterface::FIELD_LOAD_CURRENT to load the most - * recent revision for all fields, or - * EntityStorageInterface::FIELD_LOAD_REVISION to load the version - * indicated by each entity. - */ - abstract protected function doLoadFieldItems($entities, $age); - - /** - * Saves values of configurable fields for an entity. - * - * This is the method that holds the actual storage logic. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - * @param bool $update - * TRUE if the entity is being updated, FALSE if it is being inserted. - */ - abstract protected function doSaveFieldItems(EntityInterface $entity, $update); - - /** - * Deletes values of configurable fields for all revisions of an entity. - * - * This is the method that holds the actual storage logic. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - */ - abstract protected function doDeleteFieldItems(EntityInterface $entity); - - /** - * Deletes values of configurable fields for a single revision of an entity. - * - * This is the method that holds the actual storage logic. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity. - */ - abstract protected function doDeleteFieldItemsRevision(EntityInterface $entity); - - /** * {@inheritdoc} */ public function onFieldCreate(FieldConfigInterface $field) { } diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php index 4382665..1c2a3b0 100644 --- a/core/lib/Drupal/Core/Entity/EntityType.php +++ b/core/lib/Drupal/Core/Entity/EntityType.php @@ -38,7 +38,7 @@ class EntityType implements EntityTypeInterface { * * @var bool */ - protected $field_cache; + protected $persistent_cache; /** * An array of entity keys. @@ -249,8 +249,8 @@ public function isRenderCacheable() { /** * {@inheritdoc} */ - public function isFieldDataCacheable() { - return isset($this->field_cache) ? $this->field_cache: TRUE; + public function isPersistentlyCacheable() { + return isset($this->persistent_cache) ? $this->persistent_cache: TRUE; } /** diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php index a6e39aa..d92e00b 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php @@ -153,7 +153,7 @@ public function isRenderCacheable(); * * @return bool */ - public function isFieldDataCacheable(); + public function isPersistentlyCacheable(); /** * Sets the name of the entity type class. diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index b09bfa8..5a3574f 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -867,11 +867,11 @@ function comment_translation_configuration_element_submit($form, &$form_state) { } /** - * Implements hook_entity_load(). + * Implements hook_entity_storage_load(). * * @see \Drupal\comment\Plugin\Field\FieldType\CommentItem::propertyDefinitions() */ -function comment_entity_load($entities, $entity_type) { +function comment_entity_storage_load($entities, $entity_type) { // Comments can only be attached to content entities, so skip others. if (!\Drupal::entityManager()->getDefinition($entity_type)->isSubclassOf('Drupal\Core\Entity\ContentEntityInterface')) { return; diff --git a/core/modules/comment/src/CommentStatistics.php b/core/modules/comment/src/CommentStatistics.php index 9e3fbd1..b20c67b 100644 --- a/core/modules/comment/src/CommentStatistics.php +++ b/core/modules/comment/src/CommentStatistics.php @@ -244,6 +244,10 @@ public function update(CommentInterface $comment) { ->condition('field_id', $comment->getFieldId()) ->execute(); } + + // Reset the cache of the commented entity so that when the entity is loaded + // the next time, the statistics will be loaded again. + $this->entityManager->getStorage($comment->getCommentedEntityTypeId())->resetCache(array($comment->getCommentedEntityId())); } } diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php index 88a1bd4..5c6c288 100644 --- a/core/modules/comment/src/CommentStorage.php +++ b/core/modules/comment/src/CommentStorage.php @@ -7,6 +7,7 @@ namespace Drupal\comment; +use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; @@ -38,11 +39,13 @@ class CommentStorage extends ContentEntityDatabaseStorage implements CommentStor * The database connection to be used. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * Cache backend instance to use. * @param \Drupal\comment\CommentStatisticsInterface $comment_statistics * The comment statistics service. */ - public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CommentStatisticsInterface $comment_statistics) { - parent::__construct($entity_info, $database, $entity_manager); + public function __construct(EntityTypeInterface $entity_info, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, CommentStatisticsInterface $comment_statistics) { + parent::__construct($entity_info, $database, $entity_manager, $cache); $this->statistics = $comment_statistics; } @@ -54,6 +57,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI $entity_info, $container->get('database'), $container->get('entity.manager'), + $container->get('cache.entity'), $container->get('comment.statistics') ); } diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 4e29f4c..001a418 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -501,9 +501,9 @@ function content_translation_language_fallback_candidates_entity_view_alter(&$ca } /** - * Implements hook_entity_load(). + * Implements hook_entity_storage_load(). */ -function content_translation_entity_load(array $entities, $entity_type) { +function content_translation_entity_storage_load(array $entities, $entity_type) { $enabled_entities = array(); if (content_translation_enabled($entity_type)) { diff --git a/core/modules/field/src/Tests/FieldAttachOtherTest.php b/core/modules/field/src/Tests/FieldAttachOtherTest.php index a748939..1a9dd5e 100644 --- a/core/modules/field/src/Tests/FieldAttachOtherTest.php +++ b/core/modules/field/src/Tests/FieldAttachOtherTest.php @@ -180,12 +180,12 @@ function testEntityDisplayViewMultiple() { function testFieldAttachCache() { // Initialize random values and a test entity. $entity_init = entity_create('entity_test', array('type' => $this->instance->bundle)); - $langcode = Language::LANGCODE_NOT_SPECIFIED; + $langcode = Language::LANGCODE_DEFAULT; $values = $this->_generateTestFieldValues($this->field->getCardinality()); // Non-cacheable entity type. $entity_type = 'entity_test'; - $cid = "field:$entity_type:" . $entity_init->id(); + $cid = "values:$entity_type:" . $entity_init->id(); // Check that no initial cache entry is present. $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no initial cache entry'); @@ -194,7 +194,7 @@ function testFieldAttachCache() { $entity = clone($entity_init); $entity->{$this->field_name}->setValue($values); $entity = $this->entitySaveReload($entity); - $cid = "field:$entity_type:" . $entity->id(); + $cid = "values:$entity_type:" . $entity->id(); $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Non-cached: no cache entry on insert and load'); // Cacheable entity type. @@ -206,14 +206,14 @@ function testFieldAttachCache() { )); // Check that no initial cache entry is present. - $cid = "field:$entity_type:" . $entity->id(); + $cid = "values:$entity_type:" . $entity->id(); $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no initial cache entry'); // Save, and check that no cache entry is present. $entity = clone($entity_init); $entity->{$this->field_name_2} = $values; $entity->save(); - $cid = "field:$entity_type:" . $entity->id(); + $cid = "values:$entity_type:" . $entity->id(); $this->assertFalse(\Drupal::cache('entity')->get($cid), 'Cached: no cache entry on insert'); // Load, and check that a cache entry is present with the expected values. @@ -221,7 +221,7 @@ function testFieldAttachCache() { $controller->resetCache(); $controller->load($entity->id()); $cache = \Drupal::cache('entity')->get($cid); - $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load'); + $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load'); // Update with different values, and check that the cache entry is wiped. $values = $this->_generateTestFieldValues($this->field_2->getCardinality()); @@ -233,7 +233,7 @@ function testFieldAttachCache() { $controller->resetCache(); $controller->load($entity->id()); $cache = \Drupal::cache('entity')->get($cid); - $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load'); + $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load'); // Create a new revision, and check that the cache entry is wiped. $values = $this->_generateTestFieldValues($this->field_2->getCardinality()); @@ -246,7 +246,7 @@ function testFieldAttachCache() { $controller->resetCache(); $controller->load($entity->id()); $cache = \Drupal::cache('entity')->get($cid); - $this->assertEqual($cache->data[$langcode][$this->field_name_2], $values, 'Cached: correct cache entry on load'); + $this->assertEqual($cache->data['values'][$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load'); // Delete, and check that the cache entry is wiped. $entity->delete(); diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 47b72ef..9f1deb7 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -1914,15 +1914,14 @@ function file_get_file_references(File $file, FieldDefinitionInterface $field = $file_fields[$entity_type_id][$bundle] = array(); // This contains the possible field names. foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) { - $field_type = $field_definition->getType(); // If this is the first time this field type is seen, check // whether it references files. - if (!isset($field_columns[$field_type])) { - $field_columns[$field_type] = file_field_find_file_reference_column($field_definition); + if (!isset($field_columns[$field_definition->getType()])) { + $field_columns[$field_definition->getType()] = file_field_find_file_reference_column($field_definition); } // If the field type does reference files then record it. - if ($field_columns[$field_type]) { - $file_fields[$entity_type_id][$bundle][$field_name] = $field_columns[$field_type]; + if ($field_columns[$field_definition->getType()]) { + $file_fields[$entity_type_id][$bundle][$field_name] = $field_columns[$field_definition->getType()]; } } } diff --git a/core/modules/file/src/Tests/FilePrivateTest.php b/core/modules/file/src/Tests/FilePrivateTest.php index afb0114..9ce60a5 100644 --- a/core/modules/file/src/Tests/FilePrivateTest.php +++ b/core/modules/file/src/Tests/FilePrivateTest.php @@ -31,6 +31,7 @@ public static function getInfo() { public function setUp() { parent::setUp(); + node_access_test_add_field(entity_load('node_type', 'article')); node_access_rebuild(); \Drupal::state()->set('node_access_test.private', TRUE); } diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 1b9b8f8..2d55ba9 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -383,9 +383,9 @@ function forum_node_predelete(EntityInterface $node) { } /** - * Implements hook_node_load(). + * Implements hook_node_storage_load(). */ -function forum_node_load($nodes) { +function forum_node_storage_load($nodes) { $node_vids = array(); foreach ($nodes as $node) { if (\Drupal::service('forum_manager')->checkNodeType($node)) { diff --git a/core/modules/forum/src/Tests/ForumNodeAccessTest.php b/core/modules/forum/src/Tests/ForumNodeAccessTest.php index 99f8b25..5e7ff26 100644 --- a/core/modules/forum/src/Tests/ForumNodeAccessTest.php +++ b/core/modules/forum/src/Tests/ForumNodeAccessTest.php @@ -32,6 +32,7 @@ public static function getInfo() { function setUp() { parent::setUp(); node_access_rebuild(); + node_access_test_add_field(entity_load('node_type', 'forum')); \Drupal::state()->set('node_access_test.private', TRUE); } @@ -54,7 +55,7 @@ function testForumNodeAccess() { $edit = array( 'title[0][value]' => $private_node_title, 'body[0][value]' => $this->randomName(200), - 'private' => TRUE, + 'private[0][value]' => TRUE, ); $this->drupalPostForm('node/add/forum', $edit, t('Save'), array('query' => array('forum_id' => 1))); $private_node = $this->drupalGetNodeByTitle($private_node_title); diff --git a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php index f227792..57f12b2 100644 --- a/core/modules/node/src/Tests/NodeAccessBaseTableTest.php +++ b/core/modules/node/src/Tests/NodeAccessBaseTableTest.php @@ -39,6 +39,8 @@ public static function getInfo() { public function setUp() { parent::setUp(); + node_access_test_add_field(entity_load('node_type', 'article')); + node_access_rebuild(); \Drupal::state()->set('node_access_test.private', TRUE); } @@ -75,7 +77,7 @@ function testNodeAccessBasic() { 'title[0][value]' => t('@private_public Article created by @user', array('@private_public' => $type, '@user' => $this->webUser->getUsername())), ); if ($is_private) { - $edit['private'] = TRUE; + $edit['private[0][value]'] = TRUE; $edit['body[0][value]'] = 'private node'; $edit['field_tags'] = 'private'; } @@ -85,14 +87,13 @@ function testNodeAccessBasic() { } $this->drupalPostForm('node/add/article', $edit, t('Save')); - $nid = db_query('SELECT nid FROM {node_field_data} WHERE title = :title', array(':title' => $edit['title[0][value]']))->fetchField(); - $private_status = db_query('SELECT private FROM {node_access_test} where nid = :nid', array(':nid' => $nid))->fetchField(); - $this->assertTrue($is_private == $private_status, 'The private status of the node was properly set in the node_access_test table.'); + $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); + $this->assertEqual($is_private, (int)$node->private->value, 'The private status of the node was properly set in the node_access_test table.'); if ($is_private) { - $private_nodes[] = $nid; + $private_nodes[] = $node->id(); } - $titles[$nid] = $edit['title[0][value]']; - $this->nodesByUser[$this->webUser->id()][$nid] = $is_private; + $titles[$node->id()] = $edit['title[0][value]']; + $this->nodesByUser[$this->webUser->id()][$node->id()] = $is_private; } } $this->publicTid = db_query('SELECT tid FROM {taxonomy_term_data} WHERE name = :name', array(':name' => 'public'))->fetchField(); diff --git a/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php b/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php index 4ea9fe8..46fdbbb 100644 --- a/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php +++ b/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php @@ -53,6 +53,8 @@ public static function getInfo() { public function setUp() { parent::setUp(); + node_access_test_add_field(entity_load('node_type', 'page')); + // Create the 'private' field, which allows the node to be marked as private // (restricted access) in a given translation. $field_private = entity_create('field_config', array( diff --git a/core/modules/node/src/Tests/NodeAccessLanguageTest.php b/core/modules/node/src/Tests/NodeAccessLanguageTest.php index feeae45..1de38ec 100644 --- a/core/modules/node/src/Tests/NodeAccessLanguageTest.php +++ b/core/modules/node/src/Tests/NodeAccessLanguageTest.php @@ -32,6 +32,8 @@ public static function getInfo() { function setUp() { parent::setUp(); + node_access_test_add_field(entity_load('node_type', 'page')); + // After enabling a node access module, the access table has to be rebuild. node_access_rebuild(); diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.install b/core/modules/node/tests/modules/node_access_test/node_access_test.install deleted file mode 100644 index 6b3ef5d..0000000 --- a/core/modules/node/tests/modules/node_access_test/node_access_test.install +++ /dev/null @@ -1,42 +0,0 @@ - 'The base table for node_access_test.', - 'fields' => array( - 'nid' => array( - 'description' => 'The {node}.nid this record affects.', - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'private' => array( - 'description' => 'Boolean indicating whether the node is private (visible to administrator) or not (visible to non-administrators).', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'nid' => array('nid'), - ), - 'primary key' => array('nid'), - 'foreign keys' => array( - 'versioned_node' => array( - 'table' => 'node', - 'columns' => array('nid' => 'nid'), - ), - ), - ); - - return $schema; -} \ No newline at end of file diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module index 0d9494e..873921b 100644 --- a/core/modules/node/tests/modules/node_access_test/node_access_test.module +++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module @@ -9,8 +9,7 @@ * a special 'node test view' permission. */ -use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Field\FieldDefinition; +use Drupal\node\NodeTypeInterface; use Drupal\node\NodeInterface; /** @@ -79,74 +78,32 @@ function node_access_test_permission() { } /** - * Implements hook_entity_base_field_info(). + * Adds the private field to a node type + * + * @param \Drupal\node\NodeTypeInterface $type + * A node type object. */ -function node_access_test_entity_base_field_info(EntityTypeInterface $entity_type) { - if ($entity_type->id() === 'node') { - $fields['private'] = FieldDefinition::create('boolean') - ->setLabel(t('Private')) - ->setComputed(TRUE); - - return $fields; - } -} - -/** - * Implements hook_form_BASE_FORM_ID_alter(). - */ -function node_access_test_form_node_form_alter(&$form, $form_state) { - // Only show this checkbox for NodeAccessBaseTableTestCase. - if (\Drupal::state()->get('node_access_test.private')) { - $node = $form_state['controller']->getEntity($form_state); - $form['private'] = array( - '#type' => 'checkbox', - '#title' => t('Private'), - '#description' => t('Check here if this content should be set private and only shown to privileged users.'), - '#default_value' => $node->private->value, - ); - } -} - -/** - * Implements hook_node_load(). - */ -function node_access_test_node_load($nodes) { - $result = db_query('SELECT nid, private FROM {node_access_test} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes))); - foreach ($result as $record) { - $nodes[$record->nid]->private = $record->private; - } -} - -/** - * Implements hook_node_predelete(). - */ - -function node_access_test_node_predelete(NodeInterface $node) { - db_delete('node_access_test')->condition('nid', $node->id())->execute(); -} - -/** - * Implements hook_node_insert(). - */ -function node_access_test_node_insert(NodeInterface $node) { - _node_access_test_node_write($node); -} - -/** - * Implements hook_nodeapi_update(). - */ -function node_access_test_node_update(NodeInterface $node) { - _node_access_test_node_write($node); -} - -/** - * Helper for node insert/update. - */ -function _node_access_test_node_write(NodeInterface $node) { - db_merge('node_access_test') - ->key('nid', $node->id()) - ->fields(array('private' => (int) $node->private->value)) - ->execute(); +function node_access_test_add_field(NodeTypeInterface $type) { + $field = entity_create('field_config', array( + 'name' => 'private', + 'entity_type' => 'node', + 'type' => 'integer', + )); + $field->save(); + $instance = entity_create('field_instance_config', array( + 'field_name' => 'private', + 'entity_type' => 'node', + 'bundle' => $type->id(), + 'label' => 'Private', + )); + $instance->save(); + + // Assign widget settings for the 'default' form mode. + entity_get_form_display('node', $type->id(), 'default') + ->setComponent('private', array( + 'type' => 'number', + )) + ->save(); } /** diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 8ea735b..f03b28f 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -224,9 +224,9 @@ function rdf_entity_prepare_view($entity_type, array $entities, array $displays) } /** - * Implements hook_comment_load(). + * Implements hook_comment_storage_load(). */ -function rdf_comment_load($comments) { +function rdf_comment_storage_load($comments) { foreach ($comments as $comment) { // Pages with many comments can show poor performance. This information // isn't needed until rdf_preprocess_comment() is called, but set it here diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index 50b9f6a..c10b90d 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -269,18 +269,55 @@ function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) { * This is a generic load hook called for all entity types loaded via the * entity API. * - * @param array $entities + * The hook will not be invoked for entities loaded from the cache. Use + * hook_entity_load_uncached() if necessary. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities * The entities keyed by entity ID. * @param string $entity_type_id * The type of entities being loaded (i.e. node, user, comment). */ -function hook_entity_load($entities, $entity_type_id) { +function hook_entity_load(array $entities, $entity_type_id) { foreach ($entities as $entity) { $entity->foo = mymodule_add_something($entity); } } /** + * Act on entities when loaded from the storage or cache. + * + * Only use this hook if the result must not be cached. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The entities keyed by entity ID. + * @param string $entity_type + * The type of entities being loaded (i.e. node, user, comment). + * + * @see hook_entity_load_uncached() + */ +function hook_entity_load_uncached(array $entities, $entity_type) { + foreach ($entities as $entity) { + $entity->foo = mymodule_add_something_uncached($entity); + } +} + +/** + * Act on entities of the given type when loaded from the storage or cache. + * + * Only use this hook if the result must not be cached. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The entities keyed by entity ID. + * + * @see hook_entity_load_uncached() + */ +function hook_ENTITY_TYPE_load_uncached(array $entities) { + foreach ($entities as $entity) { + $entity->foo = mymodule_add_something_uncached($entity); + } +} + +/** * Act on an entity before it is about to be created or updated. * * @param \Drupal\Core\Entity\EntityInterface $entity diff --git a/core/modules/system/src/Tests/Update/UpdateScriptTest.php b/core/modules/system/src/Tests/Update/UpdateScriptTest.php index cd92191..b533d7c 100644 --- a/core/modules/system/src/Tests/Update/UpdateScriptTest.php +++ b/core/modules/system/src/Tests/Update/UpdateScriptTest.php @@ -83,11 +83,7 @@ function testUpdateAccess() { $this->assertResponse(200); // Access the update page as user 1. - $user1 = user_load(1); - $user1->pass_raw = user_password(); - $user1->pass = $this->container->get('password')->hash(trim($user1->pass_raw)); - db_query("UPDATE {users} SET pass = :pass WHERE uid = :uid", array(':pass' => $user1->getPassword(), ':uid' => $user1->id())); - $this->drupalLogin($user1); + $this->drupalLogin($this->root_user); $this->drupalGet($this->update_url, array('external' => TRUE)); $this->assertResponse(200); } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php index 01e6872..7b7efaf 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php @@ -32,7 +32,7 @@ * }, * base_table = "entity_test", * fieldable = TRUE, - * field_cache = FALSE, + * persistent_cache = FALSE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCache.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCache.php index 07d0e75..ab5fdab 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCache.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestCache.php @@ -22,7 +22,6 @@ * }, * base_table = "entity_test", * fieldable = TRUE, - * field_cache = TRUE, * entity_keys = { * "id" = "id", * "uuid" = "uuid", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php index 481711f..59d4574 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php @@ -13,7 +13,7 @@ * @ContentEntityType( * id = "entity_test_label_callback", * label = @Translation("Entity test label callback"), - * field_cache = FALSE, + * persistent_cache = FALSE, * base_table = "entity_test", * revision_table = "entity_test_revision", * label_callback = "entity_test_label_callback", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoLabel.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoLabel.php index 6dc35d4..5ad3ba7 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoLabel.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoLabel.php @@ -13,7 +13,7 @@ * @ContentEntityType( * id = "entity_test_no_label", * label = @Translation("Entity Test without label"), - * field_cache = FALSE, + * persistent_cache = FALSE, * base_table = "entity_test", * entity_keys = { * "id" = "id", diff --git a/core/modules/text/src/Tests/TextWithSummaryItemTest.php b/core/modules/text/src/Tests/TextWithSummaryItemTest.php index 9b93095..d431776 100644 --- a/core/modules/text/src/Tests/TextWithSummaryItemTest.php +++ b/core/modules/text/src/Tests/TextWithSummaryItemTest.php @@ -126,38 +126,36 @@ function testProcessedCache() { // Load the entity and check that the field cache contains the expected // data. $entity = entity_load($entity_type, $entity->id()); - $cache = \Drupal::cache('entity')->get("field:$entity_type:" . $entity->id()); - $this->assertEqual($cache->data, array( - Language::LANGCODE_NOT_SPECIFIED => array( - 'summary_field' => array( - 0 => array( - 'value' => $value, - 'summary' => $summary, - 'format' => 'plain_text', - 'processed' => $value, - 'summary_processed' => $summary, - ), + $cache = \Drupal::cache('entity')->get("values:$entity_type:" . $entity->id()); + $this->assertEqual($cache->data['values']['summary_field'], array( + Language::LANGCODE_DEFAULT => array( + 0 => array( + 'value' => $value, + 'summary' => $summary, + 'format' => 'plain_text', + 'processed' => $value, + 'summary_processed' => $summary, ), ), )); // Inject fake processed values into the cache to make sure that these are // used as-is and not re-calculated when the entity is loaded. - $data = array( - Language::LANGCODE_NOT_SPECIFIED => array( - 'summary_field' => array( - 0 => array( - 'value' => $value, - 'summary' => $summary, - 'format' => 'plain_text', - 'processed' => 'Cached processed value', - 'summary_processed' => 'Cached summary processed value', - ), + $data = $cache->data; + $data['values']['summary_field'] = array( + Language::LANGCODE_DEFAULT => array( + 0 => array( + 'value' => $value, + 'summary' => $summary, + 'format' => 'plain_text', + 'processed' => 'Cached processed value', + 'summary_processed' => 'Cached summary processed value', ), ), ); - \Drupal::cache('entity')->set("field:$entity_type:" . $entity->id(), $data); - $entity = entity_load($entity_type, $entity->id(), TRUE); + \Drupal::entityManager()->getStorage($entity_type)->resetCache(); + \Drupal::cache('entity')->set("values:$entity_type:" . $entity->id(), $data); + $entity = entity_load($entity_type, $entity->id()); $this->assertEqual($entity->summary_field->processed, 'Cached processed value'); $this->assertEqual($entity->summary_field->summary_processed, 'Cached summary processed value'); diff --git a/core/modules/tracker/src/Tests/TrackerNodeAccessTest.php b/core/modules/tracker/src/Tests/TrackerNodeAccessTest.php index c50eed2..ed5f58e 100644 --- a/core/modules/tracker/src/Tests/TrackerNodeAccessTest.php +++ b/core/modules/tracker/src/Tests/TrackerNodeAccessTest.php @@ -34,6 +34,7 @@ public function setUp() { parent::setUp(); node_access_rebuild(); $this->drupalCreateContentType(array('type' => 'page')); + node_access_test_add_field(entity_load('node_type', 'page')); $this->container->get('comment.manager')->addDefaultField('node', 'page', 'comment', CommentItemInterface::OPEN); \Drupal::state()->set('node_access_test.private', TRUE); } diff --git a/core/modules/user/src/UserStorage.php b/core/modules/user/src/UserStorage.php index d5d91ca..6414ea7 100644 --- a/core/modules/user/src/UserStorage.php +++ b/core/modules/user/src/UserStorage.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\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -32,13 +33,6 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt protected $password; /** - * Provides the user data service object. - * - * @var \Drupal\user\UserDataInterface - */ - protected $userData; - - /** * Constructs a new UserStorage object. * * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type @@ -47,18 +41,15 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt * The database connection to be used. * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager * The entity manager. - * @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(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) { - parent::__construct($entity_type, $database, $entity_manager, $uuid_service); + public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, PasswordInterface $password) { + parent::__construct($entity_type, $database, $entity_manager, $cache); $this->password = $password; - $this->userData = $user_data; } /** @@ -69,9 +60,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI $entity_type, $container->get('database'), $container->get('entity.manager'), - $container->get('uuid'), - $container->get('password'), - $container->get('user.data') + $container->get('cache.entity'), + $container->get('password') ); } @@ -152,6 +142,8 @@ public function updateLastLoginTimestamp(UserInterface $account) { ->fields(array('login' => $account->getLastLoginTime())) ->condition('uid', $account->id()) ->execute(); + // Ensure that the entity cache is cleared. + $this->resetCache(array($account->id())); } } diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php index 6e60480..ae44364 100644 --- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php @@ -152,7 +152,8 @@ public function testCreate() { $connection = $this->getMockBuilder('Drupal\Core\Database\Connection') ->disableOriginalConstructor() ->getMock(); - $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager); + $cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); + $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager, $cache); $entity = $entity_storage->create(); $entity->expects($this->atLeastOnce())