Issue #2381217 by Wim Leers, dawehner, Fabianx: Views should set cache tags on its render arrays, and bubble the output's cache tags to the cache items written to the Views output cache
parent
94fb2afd31
commit
9309d3d9f5
|
@ -544,11 +544,6 @@ class Renderer implements RendererInterface {
|
|||
|
||||
$data = $this->getCacheableRenderArray($elements);
|
||||
|
||||
// Cache tags are cached, but we also want to associate the "rendered" cache
|
||||
// tag. This allows us to invalidate the entire render cache, regardless of
|
||||
// the cache bin.
|
||||
$data['#cache']['tags'][] = 'rendered';
|
||||
|
||||
$bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
|
||||
$expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
|
||||
$cache = $this->cacheFactory->get($bin);
|
||||
|
@ -690,7 +685,7 @@ class Renderer implements RendererInterface {
|
|||
'tags' => Cache::mergeTags($stored_cache_tags, $data['#cache']['tags']),
|
||||
],
|
||||
];
|
||||
$cache->set($pre_bubbling_cid, $redirect_data, $expire, $redirect_data['#cache']['tags']);
|
||||
$cache->set($pre_bubbling_cid, $redirect_data, $expire, Cache::mergeTags($redirect_data['#cache']['tags'], ['rendered']));
|
||||
}
|
||||
|
||||
// Current cache contexts incomplete: this request only uses a subset of
|
||||
|
@ -711,7 +706,7 @@ class Renderer implements RendererInterface {
|
|||
$data['#cache']['contexts'] = $merged_cache_contexts;
|
||||
}
|
||||
}
|
||||
$cache->set($cid, $data, $expire, $data['#cache']['tags']);
|
||||
$cache->set($cid, $data, $expire, Cache::mergeTags($data['#cache']['tags'], ['rendered']));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
namespace Drupal\node\Tests\Views;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\views\Tests\AssertViewsCacheTagsTrait;
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Drupal\views\Views;
|
||||
|
@ -18,6 +23,14 @@ use Drupal\views\Views;
|
|||
*/
|
||||
class FrontPageTest extends ViewTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
use AssertViewsCacheTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
||||
/**
|
||||
* The entity storage for nodes.
|
||||
*
|
||||
|
@ -35,7 +48,8 @@ class FrontPageTest extends ViewTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->nodeStorage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$this->nodeStorage = $this->container->get('entity.manager')
|
||||
->getStorage('node');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,4 +187,166 @@ class FrontPageTest extends ViewTestBase {
|
|||
$this->assertPattern('/class=".+view-frontpage/', 'Frontpage view was rendered');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "none" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginNone() {
|
||||
$this->enablePageCaching();
|
||||
$this->assertFrontPageViewCacheTags(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "tag" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginTag() {
|
||||
$this->enablePageCaching();
|
||||
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
$view->display_handler->overrideOption('cache', [
|
||||
'type' => 'tag',
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertFrontPageViewCacheTags(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags when using the "time" cache plugin.
|
||||
*/
|
||||
public function testCacheTagsWithCachePluginTime() {
|
||||
$this->enablePageCaching();
|
||||
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
$view->display_handler->overrideOption('cache', [
|
||||
'type' => 'time',
|
||||
'options' => [
|
||||
'results_lifespan' => 3600,
|
||||
'output_lifespan' => 3600,
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertFrontPageViewCacheTags(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the cache tags on the front page.
|
||||
*
|
||||
* @param bool $do_assert_views_caches
|
||||
* Whether to check Views' result & output caches.
|
||||
*/
|
||||
protected function assertFrontPageViewCacheTags($do_assert_views_caches) {
|
||||
$view = Views::getView('frontpage');
|
||||
$view->setDisplay('page_1');
|
||||
|
||||
$cache_contexts = [];
|
||||
|
||||
// Test before there are any nodes.
|
||||
$empty_node_listing_cache_tags = [
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
];
|
||||
$this->assertViewsCacheTags(
|
||||
$view,
|
||||
$empty_node_listing_cache_tags,
|
||||
$do_assert_views_caches,
|
||||
$empty_node_listing_cache_tags
|
||||
);
|
||||
$this->assertPageCacheContextsAndTags(
|
||||
Url::fromRoute('view.frontpage.page_1'),
|
||||
$cache_contexts,
|
||||
Cache::mergeTags($empty_node_listing_cache_tags, ['rendered'])
|
||||
);
|
||||
|
||||
// Create some nodes on the frontpage view. Add more than 10 nodes in order
|
||||
// to enable paging.
|
||||
$this->drupalCreateContentType(['type' => 'article']);
|
||||
for ($i = 0; $i < 15; $i++) {
|
||||
$node = Node::create([
|
||||
'body' => [
|
||||
[
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
]
|
||||
],
|
||||
'type' => 'article',
|
||||
'created' => $i,
|
||||
'title' => $this->randomMachineName(8),
|
||||
'nid' => $i + 1,
|
||||
]);
|
||||
$node->enforceIsNew(TRUE);
|
||||
$node->save();
|
||||
}
|
||||
$cache_contexts = Cache::mergeContexts($cache_contexts, [
|
||||
'theme',
|
||||
'timezone',
|
||||
'user.roles'
|
||||
]);
|
||||
|
||||
// First page.
|
||||
$first_page_result_cache_tags = [
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
'node:6',
|
||||
'node:7',
|
||||
'node:8',
|
||||
'node:9',
|
||||
'node:10',
|
||||
'node:11',
|
||||
'node:12',
|
||||
'node:13',
|
||||
'node:14',
|
||||
'node:15',
|
||||
];
|
||||
$first_page_output_cache_tags = Cache::mergeTags($first_page_result_cache_tags, [
|
||||
'config:filter.format.plain_text',
|
||||
'node_view',
|
||||
'user_view',
|
||||
'user:0',
|
||||
]);
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(0);
|
||||
$this->assertViewsCacheTags(
|
||||
$view,
|
||||
$first_page_result_cache_tags,
|
||||
$do_assert_views_caches,
|
||||
$first_page_output_cache_tags
|
||||
);
|
||||
$this->assertPageCacheContextsAndTags(
|
||||
Url::fromRoute('view.frontpage.page_1'),
|
||||
$cache_contexts,
|
||||
Cache::mergeTags($first_page_output_cache_tags, ['rendered'])
|
||||
);
|
||||
|
||||
// Second page.
|
||||
$this->assertPageCacheContextsAndTags(Url::fromRoute('view.frontpage.page_1', [], ['query' => ['page' => 1]]), $cache_contexts, [
|
||||
// The cache tags for the listed nodes.
|
||||
'node:1',
|
||||
'node:2',
|
||||
'node:3',
|
||||
'node:4',
|
||||
'node:5',
|
||||
// The rest.
|
||||
'config:filter.format.plain_text',
|
||||
'config:views.view.frontpage',
|
||||
'node_list',
|
||||
'node_view',
|
||||
'user_view',
|
||||
'user:0',
|
||||
'rendered',
|
||||
]);
|
||||
|
||||
// Let's update a node title on the first page and ensure that the page
|
||||
// cache entry invalidates.
|
||||
$node = Node::load(10);
|
||||
$title = $node->getTitle() . 'a';
|
||||
$node->setTitle($title);
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet(Url::fromRoute('view.frontpage.page_1'));
|
||||
$this->assertText($title);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Cache;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
|
||||
/**
|
||||
* Provides test assertions for testing page-level cache contexts & tags.
|
||||
*
|
||||
* Can be used by test classes that extend \Drupal\simpletest\WebTestBase.
|
||||
*/
|
||||
trait AssertPageCacheContextsAndTagsTrait {
|
||||
|
||||
/**
|
||||
* Enables page caching.
|
||||
*/
|
||||
protected function enablePageCaching() {
|
||||
$config = $this->config('system.performance');
|
||||
$config->set('cache.page.use_internal', 1);
|
||||
$config->set('cache.page.max_age', 300);
|
||||
$config->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts page cache miss, then hit for the given URL; checks cache headers.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL to test.
|
||||
* @param string[] $expected_contexts
|
||||
* The expected cache contexts for the given URL.
|
||||
* @param string[] $expected_tags
|
||||
* The expected cache tags for the given URL.
|
||||
*/
|
||||
protected function assertPageCacheContextsAndTags(Url $url, array $expected_contexts, array $expected_tags) {
|
||||
$absolute_url = $url->setAbsolute()->toString();
|
||||
sort($expected_contexts);
|
||||
sort($expected_tags);
|
||||
|
||||
$get_cache_header_values = function ($header_name) {
|
||||
$header_value = $this->drupalGetHeader($header_name);
|
||||
if (empty($header_value)) {
|
||||
return [];
|
||||
}
|
||||
else {
|
||||
return explode(' ', $header_value);
|
||||
}
|
||||
};
|
||||
|
||||
// Assert cache miss + expected cache contexts + tags.
|
||||
$this->drupalGet($absolute_url);
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
$actual_contexts = $get_cache_header_values('X-Drupal-Cache-Contexts');
|
||||
$actual_tags = $get_cache_header_values('X-Drupal-Cache-Tags');
|
||||
$this->assertIdentical($actual_contexts, $expected_contexts);
|
||||
if ($actual_contexts !== $expected_contexts) {
|
||||
debug(array_diff($actual_contexts, $expected_contexts));
|
||||
}
|
||||
$this->assertIdentical($actual_tags, $expected_tags);
|
||||
if ($actual_tags !== $expected_tags) {
|
||||
debug(array_diff($actual_tags, $expected_tags));
|
||||
}
|
||||
|
||||
// Assert cache hit + expected cache contexts + tags.
|
||||
$this->drupalGet($absolute_url);
|
||||
$actual_contexts = $get_cache_header_values('X-Drupal-Cache-Contexts');
|
||||
$actual_tags = $get_cache_header_values('X-Drupal-Cache-Tags');
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$this->assertIdentical($actual_contexts, $expected_contexts);
|
||||
if ($actual_contexts !== $expected_contexts) {
|
||||
debug(array_diff($actual_contexts, $expected_contexts));
|
||||
}
|
||||
$this->assertIdentical($actual_tags, $expected_tags);
|
||||
if ($actual_tags !== $expected_tags) {
|
||||
debug(array_diff($actual_tags, $expected_tags));
|
||||
}
|
||||
|
||||
// Assert page cache item + expected cache tags.
|
||||
$cid_parts = array($url->setAbsolute()->toString(), 'html');
|
||||
$cid = implode(':', $cid_parts);
|
||||
$cache_entry = \Drupal::cache('render')->get($cid);
|
||||
sort($cache_entry->tags);
|
||||
$this->assertEqual($cache_entry->tags, $expected_tags);
|
||||
if ($cache_entry->tags !== $expected_tags) {
|
||||
debug(array_diff($cache_entry->tags, $expected_tags));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,9 +7,7 @@
|
|||
|
||||
namespace Drupal\system\Tests\Cache;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
|
||||
/**
|
||||
* Enables the page cache and tests its cache tags in various scenarios.
|
||||
|
@ -21,6 +19,8 @@ use Drupal\Core\Cache\Cache;
|
|||
*/
|
||||
class PageCacheTagsIntegrationTest extends WebTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
|
||||
protected $profile = 'standard';
|
||||
|
||||
protected $dumpHeaders = TRUE;
|
||||
|
@ -31,10 +31,7 @@ class PageCacheTagsIntegrationTest extends WebTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$config = $this->config('system.performance');
|
||||
$config->set('cache.page.use_internal', 1);
|
||||
$config->set('cache.page.max_age', 300);
|
||||
$config->save();
|
||||
$this->enablePageCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,46 +125,10 @@ class PageCacheTagsIntegrationTest extends WebTestBase {
|
|||
'config:system.menu.footer',
|
||||
'config:system.menu.main',
|
||||
'config:system.site',
|
||||
'comment_list',
|
||||
'node_list',
|
||||
'config:views.view.comments_recent',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts page cache miss, then hit for the given URL; checks cache headers.
|
||||
*
|
||||
* @param \Drupal\Core\Url $url
|
||||
* The URL to test.
|
||||
* @param string[] $expected_contexts
|
||||
* The expected cache contexts for the given URL.
|
||||
* @param string[] $expected_tags
|
||||
* The expected cache tags for the given URL.
|
||||
*/
|
||||
protected function assertPageCacheContextsAndTags(Url $url, array $expected_contexts, array $expected_tags) {
|
||||
$absolute_url = $url->setAbsolute()->toString();
|
||||
sort($expected_contexts);
|
||||
sort($expected_tags);
|
||||
|
||||
// Assert cache miss + expected cache contexts + tags.
|
||||
$this->drupalGet($absolute_url);
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
|
||||
$actual_contexts = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Contexts'));
|
||||
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
|
||||
$this->assertIdentical($actual_contexts, $expected_contexts);
|
||||
$this->assertIdentical($actual_tags, $expected_tags);
|
||||
|
||||
// Assert cache hit + expected cache contexts + tags.
|
||||
$this->drupalGet($absolute_url);
|
||||
$actual_contexts = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Contexts'));
|
||||
$actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
|
||||
$this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
|
||||
$this->assertIdentical($actual_contexts, $expected_contexts);
|
||||
$this->assertIdentical($actual_tags, $expected_tags);
|
||||
|
||||
// Assert page cache item + expected cache tags.
|
||||
$cid_parts = array($url->setAbsolute()->toString(), 'html');
|
||||
$cid = implode(':', $cid_parts);
|
||||
$cache_entry = \Drupal::cache('render')->get($cid);
|
||||
sort($cache_entry->tags);
|
||||
$this->assertEqual($cache_entry->tags, $expected_tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ abstract class CachePluginBase extends PluginBase {
|
|||
// that is used to render the view for this request and rendering does
|
||||
// not happen twice.
|
||||
$this->storage = $this->view->display_handler->output = $this->renderer->getCacheableRenderArray($output);
|
||||
\Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->getCacheTags());
|
||||
\Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), Cache::mergeTags($this->storage['#cache']['tags'], ['rendered']));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -239,9 +239,6 @@ abstract class CachePluginBase extends PluginBase {
|
|||
|
||||
/**
|
||||
* Clear out cached data for a view.
|
||||
*
|
||||
* We're just going to nuke anything related to the view, regardless of display,
|
||||
* to be sure that we catch everything. Maybe that's a bad idea.
|
||||
*/
|
||||
public function cacheFlush() {
|
||||
Cache::invalidateTags($this->view->storage->getCacheTags());
|
||||
|
@ -301,12 +298,18 @@ abstract class CachePluginBase extends PluginBase {
|
|||
'langcode' => \Drupal::languageManager()->getCurrentLanguage()->getId(),
|
||||
'base_url' => $GLOBALS['base_url'],
|
||||
);
|
||||
foreach (array('exposed_info', 'page', 'sort', 'order', 'items_per_page', 'offset') as $key) {
|
||||
foreach (array('exposed_info', 'sort', 'order') as $key) {
|
||||
if ($this->view->getRequest()->query->has($key)) {
|
||||
$key_data[$key] = $this->view->getRequest()->query->get($key);
|
||||
}
|
||||
}
|
||||
|
||||
$key_data['pager'] = [
|
||||
'page' => $this->view->getCurrentPage(),
|
||||
'items_per_page' => $this->view->getItemsPerPage(),
|
||||
'offset' => $this->view->getOffset(),
|
||||
];
|
||||
|
||||
$this->resultsKey = $this->view->storage->id() . ':' . $this->displayHandler->display['id'] . ':results:' . hash('sha256', serialize($key_data));
|
||||
}
|
||||
|
||||
|
@ -343,18 +346,21 @@ abstract class CachePluginBase extends PluginBase {
|
|||
* @return string[]
|
||||
* An array of cache tags based on the current view.
|
||||
*/
|
||||
protected function getCacheTags() {
|
||||
public function getCacheTags() {
|
||||
$tags = $this->view->storage->getCacheTags();
|
||||
|
||||
// The list cache tags for the entity types listed in this view.
|
||||
$entity_information = $this->view->query->getEntityTableInfo();
|
||||
|
||||
if (!empty($entity_information)) {
|
||||
// Add the list cache tags for each entity type used by this view.
|
||||
foreach (array_keys($entity_information) as $entity_type) {
|
||||
$tags = Cache::mergeTags($tags, \Drupal::entityManager()->getDefinition($entity_type)->getListCacheTags());
|
||||
foreach ($entity_information as $table => $metadata) {
|
||||
$tags = Cache::mergeTags($tags, \Drupal::entityManager()->getDefinition($metadata['entity_type'])->getListCacheTags());
|
||||
}
|
||||
}
|
||||
|
||||
$tags = Cache::mergeTags($tags, $this->view->getQuery()->getCacheTags());
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
|
|
|
@ -2126,6 +2126,15 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
|
|||
'#post_render_cache' => &$this->view->element['#post_render_cache'],
|
||||
);
|
||||
|
||||
if (!isset($element['#cache'])) {
|
||||
$element['#cache'] = [];
|
||||
}
|
||||
$element['#cache'] += ['tags' => []];
|
||||
|
||||
// If the output is a render array, add cache tags, regardless of whether
|
||||
// caching is enabled or not; cache tags must always be set.
|
||||
$element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $this->view->getCacheTags());
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
|
|
|
@ -312,6 +312,13 @@ abstract class QueryPluginBase extends PluginBase {
|
|||
return $entity_tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\views\Plugin\views\query;
|
||||
|
||||
use Drupal\Component\Utility\NestedArray;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\views\Plugin\views\display\DisplayPluginBase;
|
||||
|
@ -1537,6 +1538,23 @@ class Sql extends QueryPluginBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
$tags = [];
|
||||
// Add cache tags for each row, if there is an entity associated with it.
|
||||
if (!$this->hasAggregate) {
|
||||
foreach ($this->view->result as $row) {
|
||||
if ($row->_entity) {
|
||||
$tags = Cache::mergeTags($row->_entity->getCacheTags(), $tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function addSignature(ViewExecutable $view) {
|
||||
$view->query->addField(NULL, "'" . $view->storage->id() . ':' . $view->current_display . "'", 'view_name');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\Tests\AssertViewsCacheTagsTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\views\ViewExecutable;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
trait AssertViewsCacheTagsTrait {
|
||||
|
||||
|
||||
/**
|
||||
* Asserts a view's result & output cache items' cache tags.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view to test, must have caching enabled.
|
||||
* @param null|string[] $expected_results_cache
|
||||
* NULL when expecting no results cache item, a set of cache tags expected
|
||||
* to be set on the results cache item otherwise.
|
||||
* @param bool $views_caching_is_enabled
|
||||
* Whether to expect an output cache item. If TRUE, the cache tags must
|
||||
* match those in $expected_render_array_cache_tags.
|
||||
* @param string[] $expected_render_array_cache_tags
|
||||
* A set of cache tags expected to be set on the built view's render array.
|
||||
*
|
||||
* @return array
|
||||
* The render array
|
||||
*/
|
||||
protected function assertViewsCacheTags(ViewExecutable $view, $expected_results_cache, $views_caching_is_enabled, array $expected_render_array_cache_tags) {
|
||||
$build = $view->preview();
|
||||
|
||||
// Ensure the current request is a GET request so that render caching is
|
||||
// active for direct rendering of views, just like for actual requests.
|
||||
/** @var \Symfony\Component\HttpFoundation\RequestStack $request_stack */
|
||||
$request_stack = \Drupal::service('request_stack');
|
||||
$request_stack->push(new Request());
|
||||
\Drupal::service('renderer')->renderRoot($build);
|
||||
$request_stack->pop();
|
||||
|
||||
// Render array cache tags.
|
||||
$this->pass('Checking render array cache tags.');
|
||||
sort($expected_render_array_cache_tags);
|
||||
$this->assertEqual($build['#cache']['tags'], $expected_render_array_cache_tags);
|
||||
|
||||
if ($views_caching_is_enabled) {
|
||||
$this->pass('Checking Views results cache item cache tags.');
|
||||
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */
|
||||
$cache_plugin = $view->display_handler->getPlugin('cache');
|
||||
|
||||
// Results cache.
|
||||
$results_cache_item = \Drupal::cache('data')->get($cache_plugin->generateResultsKey());
|
||||
if (is_array($expected_results_cache)) {
|
||||
$this->assertTrue($results_cache_item, 'Results cache item found.');
|
||||
if ($results_cache_item) {
|
||||
sort($expected_results_cache);
|
||||
$this->assertEqual($results_cache_item->tags, $expected_results_cache);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($results_cache_item, 'Results cache item not found.');
|
||||
}
|
||||
|
||||
// Output cache.
|
||||
$this->pass('Checking Views output cache item cache tags.');
|
||||
$output_cache_item = \Drupal::cache('render')->get($cache_plugin->generateOutputKey());
|
||||
if ($views_caching_is_enabled === TRUE) {
|
||||
$this->assertTrue($output_cache_item, 'Output cache item found.');
|
||||
if ($output_cache_item) {
|
||||
$this->assertEqual($output_cache_item->tags, Cache::mergeTags($expected_render_array_cache_tags, ['rendered']));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->assertFalse($output_cache_item, 'Output cache item not found.');
|
||||
}
|
||||
}
|
||||
|
||||
$view->destroy();
|
||||
|
||||
return $build;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ namespace Drupal\views\Tests;
|
|||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
|
@ -18,6 +19,9 @@ use Drupal\views\Views;
|
|||
*/
|
||||
class GlossaryTest extends ViewTestBase {
|
||||
|
||||
use AssertPageCacheContextsAndTagsTrait;
|
||||
use AssertViewsCacheTagsTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
|
@ -39,6 +43,7 @@ class GlossaryTest extends ViewTestBase {
|
|||
'a' => 3,
|
||||
'l' => 6,
|
||||
);
|
||||
$nodes_by_char = [];
|
||||
foreach ($nodes_per_char as $char => $count) {
|
||||
$setting = array(
|
||||
'type' => $type->id()
|
||||
|
@ -46,7 +51,8 @@ class GlossaryTest extends ViewTestBase {
|
|||
for ($i = 0; $i < $count; $i++) {
|
||||
$node = $setting;
|
||||
$node['title'] = $char . $this->randomString(3);
|
||||
$this->drupalCreateNode($node);
|
||||
$node = $this->drupalCreateNode($node);
|
||||
$nodes_by_char[$char][] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,6 +83,16 @@ class GlossaryTest extends ViewTestBase {
|
|||
$result_count = trim(str_replace(array('|', '(', ')'), '', (string) $result[0]));
|
||||
$this->assertEqual($result_count, $count, 'The expected number got rendered.');
|
||||
}
|
||||
|
||||
// Verify cache tags.
|
||||
$this->enablePageCaching();
|
||||
$this->assertPageCacheContextsAndTags(Url::fromRoute('view.glossary.page_1'), [], [
|
||||
'config:views.view.glossary',
|
||||
'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(),
|
||||
'node_list',
|
||||
'user_list',
|
||||
'rendered',
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ class CacheTest extends PluginTestBase {
|
|||
* @see views_plugin_cache_time
|
||||
*/
|
||||
public function testTimeResultCaching() {
|
||||
// Create a basic result which just 2 results.
|
||||
$view = Views::getView('test_cache');
|
||||
$view->setDisplay();
|
||||
$view->display_handler->overrideOption('cache', array(
|
||||
|
@ -55,6 +54,7 @@ class CacheTest extends PluginTestBase {
|
|||
)
|
||||
));
|
||||
|
||||
// Test the default (non-paged) display.
|
||||
$this->executeView($view);
|
||||
// Verify the result.
|
||||
$this->assertEqual(5, count($view->result), 'The number of returned rows match.');
|
||||
|
@ -67,7 +67,19 @@ class CacheTest extends PluginTestBase {
|
|||
);
|
||||
db_insert('views_test_data')->fields($record)->execute();
|
||||
|
||||
// The Result should be the same as before, because of the caching.
|
||||
// The result should be the same as before, because of the caching. (Note
|
||||
// that views_test_data records don't have associated cache tags, and hence
|
||||
// the results cache items aren't invalidated.)
|
||||
$view->destroy();
|
||||
$this->executeView($view);
|
||||
// Verify the result.
|
||||
$this->assertEqual(5, count($view->result), 'The number of returned rows match.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests result caching with a pager.
|
||||
*/
|
||||
public function testTimeResultCachingWithPager() {
|
||||
$view = Views::getView('test_cache');
|
||||
$view->setDisplay();
|
||||
$view->display_handler->overrideOption('cache', array(
|
||||
|
@ -78,9 +90,31 @@ class CacheTest extends PluginTestBase {
|
|||
)
|
||||
));
|
||||
|
||||
$mapping = ['views_test_data_name' => 'name'];
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(0);
|
||||
$this->executeView($view);
|
||||
// Verify the result.
|
||||
$this->assertEqual(5, count($view->result), 'The number of returned rows match.');
|
||||
$this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
|
||||
$view->destroy();
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(1);
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, [['name' => 'Ringo'], ['name' => 'Paul']], $mapping);
|
||||
$view->destroy();
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(0);
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping);
|
||||
$view->destroy();
|
||||
|
||||
$view->setDisplay('page_1');
|
||||
$view->setCurrentPage(2);
|
||||
$this->executeView($view);
|
||||
$this->assertIdenticalResultset($view, [['name' => 'Meredith']], $mapping);
|
||||
$view->destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +183,8 @@ class CacheTest extends PluginTestBase {
|
|||
drupal_render($output);
|
||||
$this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
|
||||
$this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
|
||||
$this->assertEqual(['views_test_data:1'], $output['#cache']['tags']);
|
||||
// Note: views_test_data_views_pre_render() adds some cache tags.
|
||||
$this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']);
|
||||
$this->assertEqual(['views_test_data_post_render_cache' => [['foo' => 'bar']]], $output['#post_render_cache']);
|
||||
$this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\Tests\RenderCacheIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\Tests;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* Tests the general integration between Views and the render cache.
|
||||
*
|
||||
* @group views
|
||||
*/
|
||||
class RenderCacheIntegrationTest extends ViewUnitTestBase {
|
||||
|
||||
use AssertViewsCacheTagsTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = ['entity_test_fields', 'entity_test_row'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['entity_test', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a field-based view's cache tags when using the "none" cache plugin.
|
||||
*/
|
||||
public function testFieldBasedViewCacheTagsWithCachePluginNone() {
|
||||
$this->assertCacheTagsForFieldBasedView(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a field-based view's cache tags when using the "tag" cache plugin.
|
||||
*/
|
||||
public function testFieldBasedViewCacheTagsWithCachePluginTag() {
|
||||
$view = Views::getview('entity_test_fields');
|
||||
$view->getDisplay()->overrideOption('cache', [
|
||||
'type' => 'tag',
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertCacheTagsForFieldBasedView(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a field-based view's cache tags when using the "time" cache plugin.
|
||||
*/
|
||||
public function testFieldBasedViewCacheTagsWithCachePluginTime() {
|
||||
$view = Views::getview('entity_test_fields');
|
||||
$view->getDisplay()->overrideOption('cache', [
|
||||
'type' => 'time',
|
||||
'options' => [
|
||||
'results_lifespan' => 3600,
|
||||
'output_lifespan' => 3600,
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertCacheTagsForFieldBasedView(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cache tags on output & result cache items for a field-based view.
|
||||
*
|
||||
* @param bool $do_assert_views_caches
|
||||
* Whether to check Views' result & output caches.
|
||||
*/
|
||||
protected function assertCacheTagsForFieldBasedView($do_assert_views_caches) {
|
||||
$this->pass('Checking cache tags for field-based view.');
|
||||
$view = Views::getview('entity_test_fields');
|
||||
|
||||
// Empty result (no entities yet).
|
||||
$base_tags = ['config:views.view.entity_test_fields', 'entity_test_list'];
|
||||
$this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags);
|
||||
|
||||
|
||||
// Non-empty result (1 entity).
|
||||
$entities[] = $entity = EntityTest::create();
|
||||
$entity->save();
|
||||
|
||||
$tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags());
|
||||
$this->assertViewsCacheTags($view, $tags_with_entity, $do_assert_views_caches, $tags_with_entity);
|
||||
|
||||
|
||||
// Paged result (more entities than the items-per-page limit).
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$entities[] = $entity = EntityTest::create();
|
||||
$entity->save();
|
||||
}
|
||||
// Page 1.
|
||||
$tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
|
||||
$this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1);
|
||||
$view->destroy();
|
||||
// Page 2.
|
||||
$view->setCurrentPage(1);
|
||||
$tags_page_2 = Cache::mergeTags($base_tags, $entities[0]->getCacheTags());
|
||||
$this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2);
|
||||
$view->destroy();
|
||||
|
||||
// Ensure that invalidation works on both pages.
|
||||
$view->setCurrentPage(1);
|
||||
$entities[0]->name->value = $random_name = $this->randomMachineName();
|
||||
$entities[0]->save();
|
||||
$build = $this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2);
|
||||
$this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE);
|
||||
$view->destroy();
|
||||
|
||||
$view->setCurrentPage(0);
|
||||
$entities[1]->name->value = $random_name = $this->randomMachineName();
|
||||
$entities[1]->save();
|
||||
$build = $this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1);
|
||||
$this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a entity-based view's cache tags when using the "none" cache plugin.
|
||||
*/
|
||||
public function testEntityBasedViewCacheTagsWithCachePluginNone() {
|
||||
$this->assertCacheTagsForEntityBasedView(FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a entity-based view's cache tags when using the "tag" cache plugin.
|
||||
*/
|
||||
public function testEntityBasedViewCacheTagsWithCachePluginTag() {
|
||||
$view = Views::getview('entity_test_row');
|
||||
$view->getDisplay()->overrideOption('cache', [
|
||||
'type' => 'tag',
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertCacheTagsForEntityBasedView(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a entity-based view's cache tags when using the "time" cache plugin.
|
||||
*/
|
||||
public function testEntityBasedViewCacheTagsWithCachePluginTime() {
|
||||
$view = Views::getview('entity_test_row');
|
||||
$view->getDisplay()->overrideOption('cache', [
|
||||
'type' => 'time',
|
||||
'options' => [
|
||||
'results_lifespan' => 3600,
|
||||
'output_lifespan' => 3600,
|
||||
],
|
||||
]);
|
||||
$view->save();
|
||||
|
||||
$this->assertCacheTagsForEntityBasedView(TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests cache tags on output & result cache items for an entity-based view.
|
||||
*/
|
||||
protected function assertCacheTagsForEntityBasedView($do_assert_views_caches) {
|
||||
$this->pass('Checking cache tags for entity-based view.');
|
||||
$view = Views::getview('entity_test_row');
|
||||
|
||||
// Empty result (no entities yet).
|
||||
$base_tags = $base_render_tags = ['config:views.view.entity_test_row', 'entity_test_list'];
|
||||
$this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags);
|
||||
|
||||
|
||||
// Non-empty result (1 entity).
|
||||
$entities[] = $entity = EntityTest::create();
|
||||
$entity->save();
|
||||
|
||||
$result_tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags());
|
||||
$render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags(), ['entity_test_view']);
|
||||
$this->assertViewsCacheTags($view, $result_tags_with_entity, $do_assert_views_caches, $render_tags_with_entity);
|
||||
|
||||
|
||||
// Paged result (more entities than the items-per-page limit).
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$entities[] = $entity = EntityTest::create();
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
$new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
|
||||
$result_tags_page_1 = Cache::mergeTags($base_tags, $new_entities_cache_tags);
|
||||
$render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags, ['entity_test_view']);
|
||||
$this->assertViewsCacheTags($view, $result_tags_page_1, $do_assert_views_caches, $render_tags_page_1);
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\views;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Routing\RouteProviderInterface;
|
||||
|
@ -1398,6 +1399,7 @@ class ViewExecutable implements \Serializable {
|
|||
}
|
||||
|
||||
$this->display_handler->output = $this->display_handler->render();
|
||||
|
||||
if ($cache) {
|
||||
$cache->cacheSet('output');
|
||||
}
|
||||
|
@ -1423,6 +1425,22 @@ class ViewExecutable implements \Serializable {
|
|||
return $this->display_handler->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache tags associated with the executed view.
|
||||
*
|
||||
* Note: The cache plugin controls the used tags, so you can override it, if
|
||||
* needed.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of cache tags.
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
$this->initDisplay();
|
||||
/** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
|
||||
$cache = $this->display_handler->getPlugin('cache');
|
||||
return $cache->getCacheTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the render array outline for the given display.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
langcode: und
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: entity_test_fields
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: none
|
||||
exposed_form:
|
||||
type: basic
|
||||
fields:
|
||||
id:
|
||||
alter:
|
||||
alter_text: false
|
||||
element_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
plugin_id: numeric
|
||||
entity_type: entity_test
|
||||
entity_field: id
|
||||
id: id
|
||||
table: entity_test
|
||||
field: id
|
||||
name:
|
||||
alter:
|
||||
alter_text: false
|
||||
ellipsis: true
|
||||
html: false
|
||||
make_link: false
|
||||
strip_tags: false
|
||||
trim: false
|
||||
word_boundary: true
|
||||
empty_zero: false
|
||||
field: name
|
||||
hide_empty: false
|
||||
id: name
|
||||
table: entity_test
|
||||
plugin_id: standard
|
||||
entity_type: entity_test
|
||||
entity_field: name
|
||||
sorts:
|
||||
id:
|
||||
table: entity_test
|
||||
id: id
|
||||
field: id
|
||||
plugin_id: standard
|
||||
entity_type: entity_test
|
||||
entity_field: id
|
||||
order: desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 5
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: fields
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
|
@ -0,0 +1,41 @@
|
|||
langcode: und
|
||||
status: true
|
||||
dependencies: { }
|
||||
id: entity_test_row
|
||||
label: ''
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: entity_test
|
||||
base_field: nid
|
||||
core: '8'
|
||||
display:
|
||||
default:
|
||||
display_options:
|
||||
access:
|
||||
type: none
|
||||
cache:
|
||||
type: none
|
||||
exposed_form:
|
||||
type: basic
|
||||
sorts:
|
||||
id:
|
||||
table: entity_test
|
||||
id: id
|
||||
field: id
|
||||
plugin_id: standard
|
||||
entity_type: entity_test
|
||||
entity_field: id
|
||||
order: desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 5
|
||||
style:
|
||||
type: default
|
||||
row:
|
||||
type: 'entity:entity_test'
|
||||
display_plugin: default
|
||||
display_title: Master
|
||||
id: default
|
||||
position: 0
|
|
@ -42,3 +42,15 @@ display:
|
|||
table: views_test_data
|
||||
field: id
|
||||
relationship: none
|
||||
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_options:
|
||||
defaults:
|
||||
pager: false
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 2
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['foo'],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -141,7 +141,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -164,7 +164,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => '',
|
||||
|
@ -204,7 +204,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['bar', 'baz', 'foo'],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -243,17 +243,14 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
// The keys + contexts this redirects to.
|
||||
'keys' => ['parent'],
|
||||
'contexts' => ['bar', 'foo'],
|
||||
// The 'rendered' cache tag is also present for the redirecting cache
|
||||
// item, to ensure it is considered to be part of the render cache
|
||||
// and thus invalidated along with everything else.
|
||||
'tags' => ['dee', 'fiddle', 'har', 'rendered', 'yar'],
|
||||
'tags' => ['dee', 'fiddle', 'har', 'yar'],
|
||||
],
|
||||
],
|
||||
'parent:bar:foo' => [
|
||||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['bar', 'foo'],
|
||||
'tags' => ['dee', 'fiddle', 'har', 'yar', 'rendered'],
|
||||
'tags' => ['dee', 'fiddle', 'har', 'yar'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -334,14 +331,14 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#cache' => [
|
||||
'keys' => ['parent'],
|
||||
'contexts' => ['user.roles'],
|
||||
'tags' => ['a', 'b', 'rendered'],
|
||||
'tags' => ['a', 'b'],
|
||||
],
|
||||
]);
|
||||
$this->assertRenderCacheItem('parent:r.A', [
|
||||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['user.roles'],
|
||||
'tags' => ['a', 'b', 'rendered'],
|
||||
'tags' => ['a', 'b'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -357,14 +354,14 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#cache' => [
|
||||
'keys' => ['parent'],
|
||||
'contexts' => ['foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c'],
|
||||
],
|
||||
]);
|
||||
$this->assertRenderCacheItem('parent:foo:r.B', [
|
||||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -388,14 +385,14 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#cache' => [
|
||||
'keys' => ['parent'],
|
||||
'contexts' => ['foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c'],
|
||||
],
|
||||
]);
|
||||
$this->assertRenderCacheItem('parent:foo:r.A', [
|
||||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'rendered'],
|
||||
'tags' => ['a', 'b'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -411,7 +408,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#cache' => [
|
||||
'keys' => ['parent'],
|
||||
'contexts' => ['bar', 'foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'd', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c', 'd'],
|
||||
],
|
||||
];
|
||||
$this->assertRenderCacheItem('parent', $final_parent_cache_item);
|
||||
|
@ -419,7 +416,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['bar', 'foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'd', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c', 'd'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -434,7 +431,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['bar', 'foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'rendered'],
|
||||
'tags' => ['a', 'b'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
@ -449,7 +446,7 @@ class RendererBubblingTest extends RendererTestBase {
|
|||
'#attached' => [],
|
||||
'#cache' => [
|
||||
'contexts' => ['bar', 'foo', 'user.roles'],
|
||||
'tags' => ['a', 'b', 'c', 'rendered'],
|
||||
'tags' => ['a', 'b', 'c'],
|
||||
],
|
||||
'#post_render_cache' => [],
|
||||
'#markup' => 'parent',
|
||||
|
|
|
@ -91,7 +91,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
'#post_render_cache' => $test_element['#post_render_cache'],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
$this->assertSame($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
@ -227,7 +227,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -322,7 +322,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -348,7 +348,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -462,7 +462,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
$this->assertSame($cached_element, $expected_element, 'The correct data is cached: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
@ -560,7 +560,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
$this->assertSame($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
@ -588,7 +588,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
$this->assertSame($cached_element, $expected_element, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
@ -619,7 +619,7 @@ class RendererPostRenderCacheTest extends RendererTestBase {
|
|||
],
|
||||
'#cache' => [
|
||||
'contexts' => [],
|
||||
'tags' => ['rendered'],
|
||||
'tags' => [],
|
||||
],
|
||||
];
|
||||
$this->assertSame($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Tests\Core\Render;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
||||
|
@ -567,9 +568,12 @@ class RendererTest extends RendererTestBase {
|
|||
'render_cache_tag',
|
||||
'render_cache_tag_child:1',
|
||||
'render_cache_tag_child:2',
|
||||
'rendered',
|
||||
];
|
||||
$this->assertEquals($expected_tags, $element['#cache']['tags'], 'Cache tags were collected from the element and its subchild.');
|
||||
|
||||
// The cache item also has a 'rendered' cache tag.
|
||||
$cache_item = $this->cacheFactory->get('render')->get('render_cache_test');
|
||||
$this->assertSame(Cache::mergeTags($expected_tags, ['rendered']), $cache_item->tags);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Tests\Core\Render;
|
||||
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\MemoryBackend;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\Renderer;
|
||||
|
@ -152,6 +153,7 @@ class RendererTestBase extends UnitTestCase {
|
|||
$this->assertNotFalse($cached, sprintf('Expected cache item "%s" exists.', $cid));
|
||||
if ($cached !== FALSE) {
|
||||
$this->assertEquals($data, $cached->data, sprintf('Cache item "%s" has the expected data.', $cid));
|
||||
$this->assertSame(Cache::mergeTags($data['#cache']['tags'], ['rendered']), $cached->tags, "The cache item's cache tags also has the 'rendered' cache tag.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue