diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index d9e1df8..b001939 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -479,7 +479,7 @@ public function onDependencyRemoval(array $dependencies) { * already invalidates it. */ protected function invalidateTagsOnSave($update) { - Cache::invalidateTags($this->getEntityType()->getListCacheTags()); + Cache::invalidateTags($this->getListCacheTags()); } /** diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index 01416ba..73fadaf 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -441,6 +441,17 @@ public function getCacheContexts() { /** * {@inheritdoc} */ + public function getListCacheTags() { + $tags = $this->getEntityType()->getListCacheTags(); + if ($this->bundle() != $this->entityTypeId) { + Cache::mergeTags($tags, [$this->entityTypeId . '_list:' . $this->bundle()]); + } + return $tags; + } + + /** + * {@inheritdoc} + */ public function getCacheTagsToInvalidate() { // @todo Add bundle-specific listing cache tag? // https://www.drupal.org/node/2145751 @@ -512,7 +523,7 @@ protected function invalidateTagsOnSave($update) { // 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->getEntityType()->getListCacheTags(); + $tags = $this->getListCacheTags(); if ($this->hasLinkTemplate('canonical')) { // Creating or updating an entity may change a cached 403 or 404 response. $tags = Cache::mergeTags($tags, ['4xx-response']); diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php index 1f476e5..529e141 100644 --- a/core/lib/Drupal/Core/Entity/EntityInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityInterface.php @@ -361,6 +361,16 @@ public function referencedEntities(); public function getOriginalId(); /** + * 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 string[] + */ + public function getListCacheTags(); + + /** * Returns the cache tags that should be used to invalidate caches. * * This will not return additional cache tags added through addCacheTags(). diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php index 2fcdce5..d9abcc5 100644 --- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php @@ -366,7 +366,7 @@ public function resetCache(array $entities = NULL) { $tags = []; foreach ($entities as $entity) { $tags = Cache::mergeTags($tags, $entity->getCacheTags()); - $tags = Cache::mergeTags($tags, $entity->getEntityType()->getListCacheTags()); + $tags = Cache::mergeTags($tags, $entity->getListCacheTags()); } Cache::invalidateTags($tags); } diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php index f23979d..759eb52 100644 --- a/core/modules/block/src/BlockViewBuilder.php +++ b/core/modules/block/src/BlockViewBuilder.php @@ -87,6 +87,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la $plugin = $entity->getPlugin(); $cache_tags = Cache::mergeTags($this->getCacheTags(), $entity->getCacheTags()); + $cache_tags = Cache::mergeTags($cache_tags, $entity->getListCacheTags()); $cache_tags = Cache::mergeTags($cache_tags, $plugin->getCacheTags()); // Create the render array for the block as a whole. diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php index d2b3031..010ad2b 100644 --- a/core/modules/block/src/Entity/Block.php +++ b/core/modules/block/src/Entity/Block.php @@ -252,6 +252,18 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) { /** * {@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 getBundleListCacheTag() { + return ['theme:' . $this->theme]; + } + + /** + * {@inheritdoc} */ public function getVisibility() { return $this->getVisibilityConditions()->getConfiguration(); diff --git a/core/modules/book/book.module b/core/modules/book/book.module index 2bba277..1edff9b 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -240,7 +240,7 @@ function book_node_view(array &$build, EntityInterface $node, EntityViewDisplayI // The book navigation is a listing of Node entities, so associate its // list cache tag for correct invalidation. '#cache' => [ - 'tags' => $node->getEntityType()->getListCacheTags(), + 'tags' => $node->getListCacheTags(), ], ); } diff --git a/core/modules/book/src/BookExport.php b/core/modules/book/src/BookExport.php index dbb62e9..b9e3d42 100644 --- a/core/modules/book/src/BookExport.php +++ b/core/modules/book/src/BookExport.php @@ -87,7 +87,7 @@ public function bookExportHtml(NodeInterface $node) { '#contents' => $contents, '#depth' => $node->book['depth'], '#cache' => [ - 'tags' => $node->getEntityType()->getListCacheTags(), + 'tags' => $node->getListCacheTags(), ], ); } diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php index ba6af78..1674f37 100644 --- a/core/modules/shortcut/src/Entity/Shortcut.php +++ b/core/modules/shortcut/src/Entity/Shortcut.php @@ -177,6 +177,13 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { /** * {@inheritdoc} */ + public function getListCacheTags() { + return $this->shortcut_set->entity->getListCacheTags(); + } + + /** + * {@inheritdoc} + */ public function getCacheTagsToInvalidate() { return $this->shortcut_set->entity->getCacheTags(); } diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php index ee4fcd8..c985253 100644 --- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php @@ -375,9 +375,9 @@ public function testReferencedEntity() { // Generate the cache tags for all two possible entity listing paths. // 1. list cache tag only (listing query has no match) // 2. list cache tag plus entity cache tag (listing query has a match) - $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags); + $empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getListCacheTags(), $page_cache_tags); - $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags()); + $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getListCacheTags(), $this->entity->getCacheTags()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity)); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); @@ -523,7 +523,7 @@ public function testReferencedEntity() { $this->verifyPageCache($non_referencing_entity_url, 'HIT'); // Special case: entity types may choose to use their bundle entity type // cache tags, to avoid having excessively granular invalidation. - $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getEntityType()->getListCacheTags() == $this->entity->getEntityType()->getListCacheTags(); + $is_special_case = $bundle_entity->getCacheTags() == $this->entity->getCacheTags() && $bundle_entity->getListCacheTags() == $this->entity->getListCacheTags(); if ($is_special_case) { $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); @@ -599,7 +599,7 @@ public function testReferencedEntity() { // there is a cache miss for both the empty entity listing and the non-empty // entity listing routes, but not for other routes. $this->pass("Test invalidation of referenced entity's list cache tag.", 'Debug'); - Cache::invalidateTags($this->entity->getEntityType()->getListCacheTags()); + Cache::invalidateTags($this->entity->getListCacheTags()); $this->verifyPageCache($empty_entity_listing_url, 'MISS'); $this->verifyPageCache($nonempty_entity_listing_url, 'MISS'); $this->verifyPageCache($referencing_entity_url, 'HIT'); @@ -642,7 +642,7 @@ public function testReferencedEntity() { $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags()); $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']); - $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing()); + $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing()); $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags); $this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags)); diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 8168bf2..db063fe 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -1359,6 +1359,13 @@ public function mergeCacheMaxAge($max_age) { /** * {@inheritdoc} */ + public function getListCacheTags() { + return $this->storage->getListCacheTags(); + } + + /** + * {@inheritdoc} + */ public function getCacheTagsToInvalidate() { return $this->storage->getCacheTagsToInvalidate(); }