summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2014-04-12 18:11:33 (GMT)
committerAlex Pott2014-04-12 18:11:33 (GMT)
commitbf61ca134d7fce1b307b8ab91cc04add9a1abc87 (patch)
tree28790bbbc880373237039ddbe2698fefc437990a
parent88f026ebab668aa35480c926fd9af7c4344f39c6 (diff)
Issue #2217749 by Wim Leers, Jalandhar, visabhishek, damiankloip: Entity base class should provide standardized cache tags and built-in invalidation.
-rw-r--r--core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php3
-rw-r--r--core/lib/Drupal/Core/Entity/Entity.php65
-rw-r--r--core/lib/Drupal/Core/Entity/EntityInterface.php19
-rw-r--r--core/lib/Drupal/Core/Entity/EntityViewBuilder.php23
-rw-r--r--core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php11
-rw-r--r--core/modules/block/lib/Drupal/block/BlockViewBuilder.php27
-rw-r--r--core/modules/block/lib/Drupal/block/Entity/Block.php37
-rw-r--r--core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php9
-rw-r--r--core/modules/filter/filter.module1
-rw-r--r--core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php8
-rw-r--r--core/modules/node/lib/Drupal/node/Entity/NodeType.php11
-rw-r--r--core/modules/system/core.api.php45
-rw-r--r--core/modules/system/lib/Drupal/system/Entity/Menu.php19
-rw-r--r--core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php2
-rw-r--r--core/modules/user/lib/Drupal/user/Entity/Role.php13
-rw-r--r--core/modules/user/lib/Drupal/user/PermissionsHash.php2
-rw-r--r--core/modules/views/lib/Drupal/views/Entity/View.php14
-rw-r--r--core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php14
-rw-r--r--core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php16
-rw-r--r--core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php78
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php42
21 files changed, 317 insertions, 142 deletions
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index 1bef225..ec90b7e 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -8,6 +8,7 @@
namespace Drupal\Core\Config\Entity;
use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Entity;
use Drupal\Core\Config\ConfigDuplicateUUIDException;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -163,6 +164,8 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
* {@inheritdoc}
*/
public function disable() {
+ // An entity was disabled, invalidate its own cache tag.
+ Cache::invalidateTags(array($this->entityTypeId => array($this->id())));
return $this->setStatus(FALSE);
}
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2300f60..ed95b23 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -7,7 +7,9 @@
namespace Drupal\Core\Entity;
+use Drupal\Core\Cache\Cache;
use Drupal\Core\DependencyInjection\DependencySerialization;
+use Drupal\Component\Utility\NestedArray;
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
@@ -357,9 +359,7 @@ abstract class Entity extends DependencySerialization implements EntityInterface
*/
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
$this->onSaveOrDelete();
- if ($update) {
- $this->onUpdateBundleEntity();
- }
+ $this->invalidateTagsOnSave($update);
}
/**
@@ -384,9 +384,7 @@ abstract class Entity extends DependencySerialization implements EntityInterface
* {@inheritdoc}
*/
public static function postDelete(EntityStorageInterface $storage, array $entities) {
- foreach ($entities as $entity) {
- $entity->onSaveOrDelete();
- }
+ self::invalidateTagsOnDelete($entities);
}
/**
@@ -403,6 +401,21 @@ abstract class Entity extends DependencySerialization implements EntityInterface
}
/**
+ * {@inheritdoc}
+ */
+ public function getCacheTag() {
+ return array($this->entityTypeId => array($this->id()));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getListCacheTags() {
+ // @todo Add bundle-specific listing cache tag? https://drupal.org/node/2145751
+ return array($this->entityTypeId . 's' => TRUE);
+ }
+
+ /**
* Acts on an entity after it was saved or deleted.
*/
protected function onSaveOrDelete() {
@@ -422,6 +435,46 @@ abstract class Entity extends DependencySerialization implements EntityInterface
}
/**
+ * Invalidates an entity's cache tags upon save.
+ *
+ * @param bool $update
+ * TRUE if the entity has been updated, or FALSE if it has been inserted.
+ */
+ protected function invalidateTagsOnSave($update) {
+ // An entity was created or updated: invalidate its list cache tags. (An
+ // updated entity may start to appear in a listing because it now meets that
+ // listing's filtering requirements. A newly created entity may start to
+ // appear in listings because it did not exist before.)
+ $tags = $this->getListCacheTags();
+ if ($update) {
+ // An existing entity was updated, also invalidate its unique cache tag.
+ $tags = NestedArray::mergeDeep($tags, $this->getCacheTag());
+ $this->onUpdateBundleEntity();
+ }
+ Cache::invalidateTags($tags);
+ }
+
+ /**
+ * Invalidates an entity's cache tags upon delete.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ * An array of entities.
+ */
+ protected static function invalidateTagsOnDelete(array $entities) {
+ $tags = array();
+ foreach ($entities as $entity) {
+ // An entity was deleted: invalidate its own cache tag, but also its list
+ // cache tags. (A deleted entity may cause changes in a paged list on
+ // other pages than the one it's on. The one it's on is handled by its own
+ // cache tag, but subsequent list pages would not be invalidated, hence we
+ // must invalidate its list cache tags as well.)
+ $tags = NestedArray::mergeDeepArray(array($tags, $entity->getCacheTag(), $entity->getListCacheTags()));
+ $entity->onSaveOrDelete();
+ }
+ Cache::invalidateTags($tags);
+ }
+
+ /**
* Acts on entities of which this entity is a bundle entity type.
*/
protected function onUpdateBundleEntity() {
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 90e4699..ab62144 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -324,4 +324,23 @@ interface EntityInterface extends AccessibleInterface {
*/
public function toArray();
+ /**
+ * The unique cache tag associated with this entity.
+ *
+ * @return array
+ * An array of cache tags.
+ */
+ public function getCacheTag();
+
+ /**
+ * The list cache tags associated with this entity.
+ *
+ * Enables code listing entities of this type to ensure that newly created
+ * entities show up immediately.
+ *
+ * @return array
+ * An array of cache tags.
+ */
+ public function getListCacheTags();
+
}
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index b4c72a4..a0b8154 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -7,6 +7,7 @@
namespace Drupal\Core\Entity;
+use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Field\FieldItemInterface;
@@ -144,11 +145,8 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
'#view_mode' => $view_mode,
'#langcode' => $langcode,
'#cache' => array(
- 'tags' => array(
- $this->entityTypeId . '_view' => TRUE,
- $this->entityTypeId => array($entity->id()),
- ),
- )
+ 'tags' => NestedArray::mergeDeep($this->getCacheTag(), $entity->getCacheTag()),
+ ),
);
// Cache the rendered output if permitted by the view mode and global entity
@@ -274,14 +272,12 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
// Always invalidate the ENTITY_TYPE_list tag.
$tags = array($this->entityTypeId . '_list' => TRUE);
foreach ($entities as $entity) {
- $id = $entity->id();
- $tags[$this->entityTypeId][$id] = $id;
- $tags[$this->entityTypeId . '_view_' . $entity->bundle()] = TRUE;
+ $tags = NestedArray::mergeDeep($tags, $entity->getCacheTag());
}
- Cache::deleteTags($tags);
+ Cache::invalidateTags($tags);
}
else {
- Cache::deleteTags(array($this->entityTypeId . '_view' => TRUE));
+ Cache::invalidateTags($this->getCacheTag());
}
}
@@ -364,4 +360,11 @@ class EntityViewBuilder extends EntityControllerBase implements EntityController
return !empty($view_modes_info[$view_mode]['cache']);
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheTag() {
+ return array($this->entityTypeId . '_view' => TRUE);
+ }
+
}
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index b41848b..aee9588 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -149,4 +149,15 @@ interface EntityViewBuilderInterface {
*/
public function viewFieldItem(FieldItemInterface $item, $display_options = array());
+ /**
+ * The cache tag associated with this entity view builder.
+ *
+ * An entity view builder is instantiated on a per-entity type basis, so the
+ * cache tags are also per-entity type.
+ *
+ * @return array
+ * An array of cache tags.
+ */
+ public function getCacheTag();
+
}
diff --git a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
index cb44851..7db7b70 100644
--- a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
+++ b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
@@ -70,14 +70,13 @@ class BlockViewBuilder extends EntityViewBuilder {
// Set cache tags; these always need to be set, whether the block is
// cacheable or not, so that the page cache is correctly informed.
- $default_cache_tags = array(
- 'content' => TRUE,
- 'block_view' => TRUE,
- 'block' => array($entity->id()),
- 'theme' => $entity->get('theme'),
- );
- $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeep($default_cache_tags, $plugin->getCacheTags());
-
+ $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeepArray(array(
+ array('content' => TRUE),
+ $this->getCacheTag(), // Block view builder cache tag.
+ $entity->getCacheTag(), // Block entity cache tag.
+ $entity->getListCacheTags(), // Block entity list cache tags.
+ $plugin->getCacheTags(), // Block plugin cache tags.
+ ));
if ($plugin->isCacheable()) {
$build[$entity_id]['#pre_render'][] = array($this, 'buildBlock');
@@ -156,16 +155,4 @@ class BlockViewBuilder extends EntityViewBuilder {
return $build;
}
- /**
- * {@inheritdoc}
- */
- public function resetCache(array $entities = NULL) {
- if (isset($entities)) {
- Cache::invalidateTags(array('block' => array_keys($entities)));
- }
- else {
- Cache::invalidateTags(array('block_view' => TRUE));
- }
- }
-
}
diff --git a/core/modules/block/lib/Drupal/block/Entity/Block.php b/core/modules/block/lib/Drupal/block/Entity/Block.php
index aa9141c..f1111fe 100644
--- a/core/modules/block/lib/Drupal/block/Entity/Block.php
+++ b/core/modules/block/lib/Drupal/block/Entity/Block.php
@@ -149,31 +149,6 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
}
/**
- * {@inheritdoc}
- */
- public function postSave(EntityStorageInterface $storage, $update = TRUE) {
- parent::postSave($storage, $update);
-
- if ($update) {
- Cache::invalidateTags(array('block' => $this->id()));
- }
- // When placing a new block, invalidate all cache entries for this theme,
- // since any page that uses this theme might be affected.
- else {
- Cache::invalidateTags(array('theme' => $this->theme));
- }
- }
-
- /**
- * {@inheritdoc}
- */
- public static function postDelete(EntityStorageInterface $storage, array $entities) {
- parent::postDelete($storage, $entities);
-
- Cache::invalidateTags(array('block' => array_keys($entities)));
- }
-
- /**
* Sorts active blocks by weight; sorts inactive blocks by name.
*/
public static function sort(ConfigEntityInterface $a, ConfigEntityInterface $b) {
@@ -202,4 +177,16 @@ class Block extends ConfigEntityBase implements BlockInterface, EntityWithPlugin
return $this->dependencies;
}
+ /**
+ * {@inheritdoc}
+ *
+ * Block configuration entities are a special case: one block entity stores
+ * the placement of one block in one theme. Instead of using an entity type-
+ * specific list cache tag like most entities, use the cache tag of the theme
+ * this block is placed in instead.
+ */
+ public function getListCacheTags() {
+ return array('theme' => $this->theme);
+ }
+
}
diff --git a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
index 2101d93..887d3ca 100644
--- a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
+++ b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
@@ -36,6 +36,8 @@ class CommentLockTest extends UnitTestCase {
$container = new ContainerBuilder();
$container->set('module_handler', $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'));
$container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
+ $container->set('cache.test', $this->getMock('Drupal\Core\Cache\CacheBackendInterface'));
+ $container->setParameter('cache_bins', array('cache.test' => 'test'));
$container->register('request', 'Symfony\Component\HttpFoundation\Request');
$lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
$cid = 2;
@@ -84,7 +86,12 @@ class CommentLockTest extends UnitTestCase {
->method('get')
->with('status')
->will($this->returnValue((object) array('value' => NULL)));
-
+ $comment->expects($this->once())
+ ->method('getCacheTag')
+ ->will($this->returnValue(array('comment' => array($cid))));
+ $comment->expects($this->once())
+ ->method('getListCacheTags')
+ ->will($this->returnValue(array('comments' => TRUE)));
$storage = $this->getMock('Drupal\comment\CommentStorageInterface');
$comment->preSave($storage);
$comment->postSave($storage);
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 2e71d68..75b781d 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -177,7 +177,6 @@ function filter_formats(AccountInterface $account = NULL) {
* @see filter_formats()
*/
function filter_formats_reset() {
- Cache::deleteTags(array('filter_formats' => TRUE));
drupal_static_reset('filter_formats');
}
diff --git a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
index 2c475a5..bd8f0f90 100644
--- a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
+++ b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
@@ -7,7 +7,6 @@
namespace Drupal\filter\Entity;
-use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Config\Entity\EntityWithPluginBagInterface;
use Drupal\Core\Entity\EntityStorageInterface;
@@ -197,7 +196,6 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface, En
// Clear the filter cache whenever a text format is disabled.
filter_formats_reset();
- Cache::deleteTags(array('filter_format' => $this->format));
return $this;
}
@@ -235,11 +233,7 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface, En
// Clear the static caches of filter_formats() and others.
filter_formats_reset();
- if ($update) {
- // Clear the filter cache whenever a text format is updated.
- Cache::deleteTags(array('filter_format' => $this->id()));
- }
- elseif (!$this->isSyncing()) {
+ if (!$update && !$this->isSyncing()) {
// Default configuration of modules and installation profiles is allowed
// to specify a list of user roles to grant access to for the new format;
// apply the defined user role permissions when a new format is inserted
diff --git a/core/modules/node/lib/Drupal/node/Entity/NodeType.php b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
index 8ca6f72..fde17a2 100644
--- a/core/modules/node/lib/Drupal/node/Entity/NodeType.php
+++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
@@ -8,7 +8,6 @@
namespace Drupal\node\Entity;
use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\node\NodeTypeInterface;
@@ -157,9 +156,6 @@ class NodeType extends ConfigEntityBase implements NodeTypeInterface {
parent::postSave($storage, $update);
if (!$update) {
- // Clear the node type cache, so the new type appears.
- Cache::deleteTags(array('node_types' => TRUE));
-
entity_invoke_bundle_hook('create', 'node', $this->id());
// Create a body if the create_body property is true and we're not in
@@ -170,9 +166,6 @@ class NodeType extends ConfigEntityBase implements NodeTypeInterface {
}
}
elseif ($this->getOriginalId() != $this->id()) {
- // Clear the node type cache to reflect the rename.
- Cache::deleteTags(array('node_types' => TRUE));
-
$update_count = node_type_update_nodes($this->getOriginalId(), $this->id());
if ($update_count) {
drupal_set_message(format_plural($update_count,
@@ -185,10 +178,6 @@ class NodeType extends ConfigEntityBase implements NodeTypeInterface {
}
entity_invoke_bundle_hook('rename', 'node', $this->getOriginalId(), $this->id());
}
- else {
- // Invalidate the cache tag of the updated node type only.
- Cache::invalidateTags(array('node_type' => $this->id()));
- }
}
/**
diff --git a/core/modules/system/core.api.php b/core/modules/system/core.api.php
index 58445e0..1ecc9c1 100644
--- a/core/modules/system/core.api.php
+++ b/core/modules/system/core.api.php
@@ -279,13 +279,29 @@
* @section tags Cache Tags
*
* The fourth argument of the set() method can be used to specify cache tags,
- * which are used to identify what type of data is included in each cache. Each
- * cache can have multiple tags, and each tag has a string key and a value. The
- * value can be:
- * - TRUE, to indicate that this type of data is present in the cache.
+ * which are used to identify what type of data is included in each cache item.
+ * Each cache item can have multiple cache tags, and each cache tag has a string
+ * key and a value. The value can be:
+ * - TRUE, to indicate that this type of data is present in the cache item.
* - An array of values. For example, the "node" tag indicates that particular
- * nodes' data is present in the cache, so its value is an array of node IDs.
- * Data that has been tagged can be deleted or invalidated as a group.
+ * node's data is present in the cache item, so its value is an array of node
+ * IDs.
+ * Data that has been tagged can be deleted or invalidated as a group: no matter
+ * the Cache ID (cid) of the cache item, no matter in which cache bin a cache
+ * item lives; as long as it is tagged with a certain cache tag, it will be
+ * deleted or invalidated.
+ *
+ * Because of that, cache tags are a solution to the cache invalidation problem:
+ * - For caching to be effective, each cache item must only be invalidated when
+ * absolutely necessary. (i.e. maximizing the cache hit ratio.)
+ * - For caching to be correct, each cache item that depends on a certain thing
+ * must be invalidated whenever that certain thing is modified.
+ *
+ * A typical scenario: a user has modified a node that appears in two views,
+ * three blocks and on twelve pages. Without cache tags, we couldn't possibly
+ * know which cache items to invalidate, so we'd have to invalidate everything:
+ * we had to sacrifice effectiveness to achieve correctness. With cache tags, we
+ * can have both.
*
* Example:
* @code
@@ -297,14 +313,25 @@
* );
* \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);
*
- * // Delete or invalidate all caches with certain tags.
+ * // Delete or invalidate all cache items with certain tags.
* \Drupal\Core\Cache\Cache::deleteTags(array('node' => array(1));
* \Drupal\Core\Cache\Cache::invalidateTags(array('user' => array(1)));
* @endcode
*
- * @todo Update cache tag deletion in https://drupal.org/node/918538
+ * Drupal is a content management system, so naturally you want changes to your
+ * content to be reflected everywhere, immediately. That's why we made sure that
+ * every entity type in Drupal 8 automatically has support for cache tags: when
+ * you save an entity, you can be sure that the cache items that have the
+ * corresponding cache tags will be invalidated.
+ * This also is the case when you define your own entity types: you'll get the
+ * exact same cache tag invalidation as any of the built-in entity types, with
+ * the ability to override any of the default behavior if needed.
+ * See \Drupal\Core\Entity\EntityInterface::getCacheTag(),
+ * \Drupal\Core\Entity\EntityInterface::getListCacheTags(),
+ * \Drupal\Core\Entity\Entity::invalidateTagsOnSave() and
+ * \Drupal\Core\Entity\Entity::invalidateTagsOnDelete().
*
- * @todo Extend entity cache tags based on https://drupal.org/node/2217749
+ * @todo Update cache tag deletion in https://drupal.org/node/918538
*
* @section configuration Configuration
*
diff --git a/core/modules/system/lib/Drupal/system/Entity/Menu.php b/core/modules/system/lib/Drupal/system/Entity/Menu.php
index 4b6cee5..2ea8dd1 100644
--- a/core/modules/system/lib/Drupal/system/Entity/Menu.php
+++ b/core/modules/system/lib/Drupal/system/Entity/Menu.php
@@ -7,7 +7,6 @@
namespace Drupal\system\Entity;
-use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\system\MenuInterface;
@@ -81,22 +80,4 @@ class Menu extends ConfigEntityBase implements MenuInterface {
return (bool) $this->locked;
}
- /**
- * {@inheritdoc}
- */
- public function postSave(EntityStorageInterface $storage, $update = TRUE) {
- parent::postSave($storage, $update);
-
- Cache::invalidateTags(array('menu' => $this->id()));
- }
-
- /**
- * {@inheritdoc}
- */
- public static function postDelete(EntityStorageInterface $storage, array $entities) {
- parent::postDelete($storage, $entities);
-
- Cache::invalidateTags(array('menu' => array_keys($entities)));
- }
-
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
index 5112cc2..e4a6a52 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
@@ -222,7 +222,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
'entity_test:' . $this->referencing_entity->id(),
// Includes the main entity's cache tags, since this entity references it.
$cache_tag,
- $view_cache_tag
+ $view_cache_tag,
);
$non_referencing_entity_cache_tags = array(
'entity_test_view:1',
diff --git a/core/modules/user/lib/Drupal/user/Entity/Role.php b/core/modules/user/lib/Drupal/user/Entity/Role.php
index 81a0ee6..d9cd07c 100644
--- a/core/modules/user/lib/Drupal/user/Entity/Role.php
+++ b/core/modules/user/lib/Drupal/user/Entity/Role.php
@@ -7,7 +7,6 @@
namespace Drupal\user\Entity;
-use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\user\RoleInterface;
@@ -134,20 +133,8 @@ class Role extends ConfigEntityBase implements RoleInterface {
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
- Cache::invalidateTags(array('role' => $this->id()));
// Clear render cache.
entity_render_cache_clear();
}
- /**
- * {@inheritdoc}
- */
- public static function postDelete(EntityStorageInterface $storage, array $entities) {
- parent::postDelete($storage, $entities);
-
- $ids = array_keys($entities);
- $storage->deleteRoleReferences($ids);
- Cache::invalidateTags(array('role' => $ids));
- }
-
}
diff --git a/core/modules/user/lib/Drupal/user/PermissionsHash.php b/core/modules/user/lib/Drupal/user/PermissionsHash.php
index 3c6fe87..1fed13e 100644
--- a/core/modules/user/lib/Drupal/user/PermissionsHash.php
+++ b/core/modules/user/lib/Drupal/user/PermissionsHash.php
@@ -58,7 +58,7 @@ class PermissionsHash implements PermissionsHashInterface {
}
else {
$permissions_hash = $this->doGenerate($sorted_roles);
- $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, array('role' => $sorted_roles));
+ $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, array('user_role' => $sorted_roles));
}
return $permissions_hash;
diff --git a/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php
index 2beb7ac..304d1f8 100644
--- a/core/modules/views/lib/Drupal/views/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Entity/View.php
@@ -7,7 +7,6 @@
namespace Drupal\views\Entity;
-use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\views\Views;
@@ -305,10 +304,7 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
- // Clear cache tags for this view.
// @todo Remove if views implements a view_builder controller.
- $id = $this->id();
- Cache::deleteTags(array('view' => array($id => $id)));
views_invalidate_cache();
}
@@ -359,17 +355,9 @@ class View extends ConfigEntityBase implements ViewStorageInterface {
parent::postDelete($storage, $entities);
$tempstore = \Drupal::service('user.tempstore')->get('views');
- $tags = array();
-
foreach ($entities as $entity) {
- $id = $entity->id();
- $tempstore->delete($id);
- $tags['view'][$id] = $id;
+ $tempstore->delete($entity->id());
}
-
- // Clear cache tags for these views.
- // @todo Remove if views implements a view_builder controller.
- Cache::deleteTags($tags);
}
/**
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
index ca3d5ec..0aabe85 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -1213,4 +1213,18 @@ class ViewUI implements ViewStorageInterface {
public function getConfigDependencyName() {
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheTag() {
+ $this->storage->getCacheTag();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getListCacheTags() {
+ $this->storage->getListCacheTags();
+ }
+
}
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index 016cc73..3c52f73 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -79,6 +79,13 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
protected $id;
/**
+ * The mocked cache backend.
+ *
+ * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $cacheBackend;
+
+ /**
* {@inheritdoc}
*/
public static function getInfo() {
@@ -120,10 +127,14 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
->with('en')
->will($this->returnValue(new Language(array('id' => 'en'))));
+ $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('uuid', $this->uuid);
$container->set('language_manager', $this->languageManager);
+ $container->set('cache.test', $this->cacheBackend);
+ $container->setParameter('cache_bins', array('cache.test' => 'test'));
\Drupal::setContainer($container);
$this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array($values, $this->entityTypeId));
@@ -333,6 +344,10 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
* @depends testSetStatus
*/
public function testDisable() {
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array($this->entityTypeId => array($this->id)));
+
$this->entity->setStatus(TRUE);
$this->assertSame($this->entity, $this->entity->disable());
$this->assertFalse($this->entity->status());
@@ -425,4 +440,5 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
$this->assertSame($this->entity->get($name), $properties[$name]);
}
}
+
}
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index 10ba1a6..caf0226 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -28,6 +28,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
protected $entityType;
/**
+ * The type ID of the entity under test.
+ *
+ * @var string
+ */
+ protected $entityTypeId;
+
+ /**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -84,6 +91,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
protected $entityManager;
/**
+ * The mocked cache backend.
+ *
+ * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $cacheBackend;
+
+ /**
* {@inheritdoc}
*/
public static function getInfo() {
@@ -101,6 +115,7 @@ class ConfigEntityStorageTest extends UnitTestCase {
parent::setUp();
$this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+ $this->entityTypeId = 'test_entity_type';
$this->entityType->expects($this->any())
->method('getKey')
->will($this->returnValueMap(array(
@@ -109,7 +124,7 @@ class ConfigEntityStorageTest extends UnitTestCase {
)));
$this->entityType->expects($this->any())
->method('id')
- ->will($this->returnValue('test_entity_type'));
+ ->will($this->returnValue($this->entityTypeId));
$this->entityType->expects($this->any())
->method('getConfigPrefix')
->will($this->returnValue('the_config_prefix'));
@@ -144,8 +159,12 @@ class ConfigEntityStorageTest extends UnitTestCase {
->with('test_entity_type')
->will($this->returnValue($this->entityType));
+ $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
+ $container->set('cache.test', $this->cacheBackend);
+ $container->setParameter('cache_bins', array('cache.test' => 'test'));
\Drupal::setContainer($container);
}
@@ -158,6 +177,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
->method('getClass')
->will($this->returnValue(get_class($this->getMockEntity())));
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_create');
@@ -183,6 +205,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
->method('getClass')
->will($this->returnValue(get_class($this->getMockEntity())));
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->moduleHandler->expects($this->at(0))
->method('invokeAll')
->with('test_entity_type_create');
@@ -221,6 +246,12 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->once())
->method('save');
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ ));
+
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
@@ -273,6 +304,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->once())
->method('save');
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ $this->entityTypeId => array('foo'), // Own cache tag.
+ ));
+
$this->configFactory->expects($this->exactly(2))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
@@ -325,6 +363,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->once())
->method('save');
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ $this->entityTypeId => array('bar'), // Own cache tag.
+ ));
+
$this->configFactory->expects($this->once())
->method('rename')
->will($this->returnValue($config_object));
@@ -362,6 +407,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
* @expectedExceptionMessage The entity does not have an ID.
*/
public function testSaveInvalid() {
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$entity = $this->getMockEntity();
$this->entityStorage->save($entity);
}
@@ -383,6 +431,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->never())
->method('save');
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
@@ -410,6 +461,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->never())
->method('save');
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.foo')
@@ -439,6 +493,12 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_object->expects($this->once())
->method('save');
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ ));
+
$this->configFactory->expects($this->once())
->method('get')
->with('the_config_prefix.baz')
@@ -482,6 +542,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
array('id', 'foo'),
)));
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->configFactory->expects($this->at(1))
->method('loadMultiple')
->with(array('the_config_prefix.foo'))
@@ -643,6 +706,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
* @covers ::deleteRevision()
*/
public function testDeleteRevision() {
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
}
@@ -669,6 +735,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
$config_map[] = array("the_config_prefix.$id", $config_object);
}
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ $this->entityTypeId => array('foo', 'bar'), // Own cache tag.
+ ));
+
$this->configFactory->expects($this->exactly(2))
->method('get')
->will($this->returnValueMap($config_map));
@@ -710,6 +783,9 @@ class ConfigEntityStorageTest extends UnitTestCase {
$this->configFactory->expects($this->never())
->method('get');
+ $this->cacheBackend->expects($this->never())
+ ->method('invalidateTags');
+
$this->entityStorage->delete(array());
}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index 47c9ad4..6ccad14 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -68,6 +68,13 @@ class EntityUnitTest extends UnitTestCase {
protected $languageManager;
/**
+ * The mocked cache backend.
+ *
+ * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected $cacheBackend;
+
+ /**
* The entity values.
*
* @var array
@@ -112,11 +119,14 @@ class EntityUnitTest extends UnitTestCase {
->with('en')
->will($this->returnValue(new Language(array('id' => 'en'))));
+ $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
$container = new ContainerBuilder();
$container->set('entity.manager', $this->entityManager);
$container->set('uuid', $this->uuid);
$container->set('language_manager', $this->languageManager);
-
+ $container->set('cache.test', $this->cacheBackend);
+ $container->setParameter('cache_bins', array('cache.test' => 'test'));
\Drupal::setContainer($container);
$this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\Entity', array($this->values, $this->entityTypeId));
@@ -280,9 +290,26 @@ class EntityUnitTest extends UnitTestCase {
* @covers ::postSave
*/
public function testPostSave() {
+ $this->cacheBackend->expects($this->at(0))
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ ));
+ $this->cacheBackend->expects($this->at(1))
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId . 's' => TRUE, // List cache tag.
+ $this->entityTypeId => array($this->values['id']), // Own cache tag.
+ ));
+
// This method is internal, so check for errors on calling it only.
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
- $this->entity->postSave($storage);
+
+ // A creation should trigger the invalidation of the "list" cache tag.
+ $this->entity->postSave($storage, FALSE);
+ // An update should trigger the invalidation of both the "list" and the
+ // "own" cache tags.
+ $this->entity->postSave($storage, TRUE);
}
/**
@@ -317,16 +344,23 @@ class EntityUnitTest extends UnitTestCase {
* @covers ::postDelete
*/
public function testPostDelete() {
+ $this->cacheBackend->expects($this->once())
+ ->method('invalidateTags')
+ ->with(array(
+ $this->entityTypeId => array($this->values['id']),
+ $this->entityTypeId . 's' => TRUE,
+ ));
$storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
$entity = $this->getMockBuilder('\Drupal\Core\Entity\Entity')
+ ->setConstructorArgs(array($this->values, $this->entityTypeId))
->setMethods(array('onSaveOrDelete'))
- ->disableOriginalConstructor()
->getMock();
$entity->expects($this->once())
->method('onSaveOrDelete');
- $this->entity->postDelete($storage, array($entity));
+ $entities = array($this->values['id'] => $entity);
+ $this->entity->postDelete($storage, $entities);
}
/**