Commit 1ec10679 authored by alexpott's avatar alexpott

Issue #2452317 by dawehner: Let views result cache use cache contexts

parent 9fd632c9
......@@ -2,7 +2,7 @@
/**
* @file
* Contains \Drupal\entity_test\Cache\EntityTestViewViewGrantsCacheContext.
* Contains \Drupal\entity_test\Cache\EntityTestViewGrantsCacheContext.
*/
namespace Drupal\entity_test\Cache;
......@@ -14,7 +14,7 @@
*
* @see \Drupal\node\Cache\NodeAccessViewGrantsCacheContext
*/
class EntityTestViewViewGrantsCacheContext implements CacheContextInterface {
class EntityTestViewGrantsCacheContext implements CacheContextInterface {
/**
* {@inheritdoc}
......
......@@ -290,25 +290,18 @@ public function generateResultsKey() {
$build_info[$index] = (string)$query;
}
}
$user = \Drupal::currentUser();
$key_data = array(
'build_info' => $build_info,
'roles' => $user->getRoles(),
'super-user' => $user->id() == 1, // special caching for super user.
'langcode' => \Drupal::languageManager()->getCurrentLanguage()->getId(),
'base_url' => $GLOBALS['base_url'],
);
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 = [
'build_info' => $build_info,
];
// @todo https://www.drupal.org/node/2433591 might solve it to not require
// the pager information here.
$key_data['pager'] = [
'page' => $this->view->getCurrentPage(),
'items_per_page' => $this->view->getItemsPerPage(),
'offset' => $this->view->getOffset(),
];
$key_data += \Drupal::service('cache_contexts')->convertTokensToKeys($this->displayHandler->getCacheMetadata()['contexts']);
$this->resultsKey = $this->view->storage->id() . ':' . $this->displayHandler->display['id'] . ':results:' . hash('sha256', serialize($key_data));
}
......
......@@ -2297,6 +2297,16 @@ public function calculateCacheMetadata () {
return [$is_cacheable, $cache_contexts];
}
/**
* {@inheritdoc}
*/
public function getCacheMetadata() {
if (!isset($this->display['cache_metadata'])) {
$this->display['cache_metadata'] = $this->calculateCacheMetadata();
}
return $this->display['cache_metadata'];
}
/**
* {@inheritdoc}
*/
......
......@@ -446,6 +446,16 @@ public function preExecute();
*/
public function calculateCacheMetadata();
/**
* Gets the cache metadata.
*
* @return array
* Returns an array:
* - first value: (boolean) Whether the display is cacheable.
* - second value: (string[]) The cache contexts the display varies by.
*/
public function getCacheMetadata();
/**
* Executes the view and returns data in the format required.
*
......
......@@ -7,8 +7,9 @@
namespace Drupal\views\Tests\Plugin;
use Drupal\node\Entity\Node;
use Drupal\views\Tests\ViewUnitTestBase;
use Drupal\views\Views;
use Drupal\views\ViewExecutable;
/**
* Tests pluggable caching for views.
......@@ -16,7 +17,7 @@
* @group views
* @see views_plugin_cache
*/
class CacheTest extends PluginTestBase {
class CacheTest extends ViewUnitTestBase {
/**
* Views used by this test.
......@@ -30,14 +31,37 @@ class CacheTest extends PluginTestBase {
*
* @var array
*/
public static $modules = array('taxonomy');
public static $modules = array('taxonomy', 'text', 'user', 'node');
protected function setUp() {
parent::setUp();
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('node');
$this->installEntitySchema('taxonomy_term');
$this->installEntitySchema('user');
}
$this->enableViewsTestModule();
/**
* {@inheritdoc}
*/
protected function viewsData() {
$data = parent::viewsData();
$data['views_test_data']['test_cache_context'] = [
'real field' => 'name',
'title' => 'Test cache context',
'filter' => [
'id' => 'views_test_test_cache_context',
],
];
return $data;
}
/**
* Tests time based caching.
*
......@@ -208,7 +232,10 @@ public function testSubqueryStringCache() {
*/
public function testCacheData() {
for ($i = 1; $i <= 5; $i++) {
$this->drupalCreateNode();
Node::create([
'title' => $this->randomMachineName(8),
'type' => 'page',
])->save();
}
$view = Views::getView('test_display');
......@@ -239,32 +266,35 @@ public function testCacheData() {
}
/**
* Tests the output caching on an actual page.
* Tests the cache context integration for views result cache.
*/
public function testCacheOutputOnPage() {
$view = Views::getView('test_display');
$view->storage->setStatus(TRUE);
$view->setDisplay('page_1');
$view->display_handler->overrideOption('cache', array(
'type' => 'time',
'options' => array(
'results_lifespan' => '3600',
'output_lifespan' => '3600'
)
));
$view->save();
$this->container->get('router.builder')->rebuildIfNeeded();
public function testCacheContextIntegration() {
$view = Views::getView('test_cache');
$view->setDisplay('page_2');
\Drupal::state()->set('views_test_cache_context', 'George');
$this->executeView($view);
$output_key = $view->getDisplay()->getPlugin('cache')->generateOutputKey();
$this->assertFalse(\Drupal::cache('render')->get($output_key));
$map = ['views_test_data_name' => 'name'];
$this->assertIdenticalResultset($view, [['name' => 'George']], $map);
$this->drupalGet('test-display');
$this->assertResponse(200);
$this->assertTrue(\Drupal::cache('render')->get($output_key));
// Update the entry in the DB to ensure that result caching works.
\Drupal::database()->update('views_test_data')
->condition('name', 'George')
->fields(['name' => 'egroeG'])
->execute();
$view = Views::getView('test_cache');
$view->setDisplay('page_2');
$this->executeView($view);
$this->assertIdenticalResultset($view, [['name' => 'George']], $map);
// Now change the cache context value, a different query should be executed.
$view = Views::getView('test_cache');
$view->setDisplay('page_2');
\Drupal::state()->set('views_test_cache_context', 'Paul');
$this->executeView($view);
$this->drupalGet('test-display');
$this->assertResponse(200);
$this->assertTrue(\Drupal::cache('render')->get($output_key));
$this->assertIdenticalResultset($view, [['name' => 'Paul']], $map);
}
}
<?php
/**
* @file
* Contains \Drupal\views\Tests\Plugin\CacheWebTest.
*/
namespace Drupal\views\Tests\Plugin;
use Drupal\views\Views;
/**
* Tests pluggable caching for views via a web test.
*
* @group views
* @see views_plugin_cache
*/
class CacheWebTest extends PluginTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_display');
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('taxonomy');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableViewsTestModule();
}
/**
* Tests the output caching on an actual page.
*/
public function testCacheOutputOnPage() {
$view = Views::getView('test_display');
$view->storage->setStatus(TRUE);
$view->setDisplay('page_1');
$view->display_handler->overrideOption('cache', array(
'type' => 'time',
'options' => array(
'results_lifespan' => '3600',
'output_lifespan' => '3600'
)
));
$view->save();
$this->container->get('router.builder')->rebuildIfNeeded();
$output_key = $view->getDisplay()->getPlugin('cache')->generateOutputKey();
$this->assertFalse(\Drupal::cache('render')->get($output_key));
$this->drupalGet('test-display');
$this->assertResponse(200);
$this->assertTrue(\Drupal::cache('render')->get($output_key));
$this->drupalGet('test-display');
$this->assertResponse(200);
$this->assertTrue(\Drupal::cache('render')->get($output_key));
}
}
......@@ -42,7 +42,6 @@ display:
table: views_test_data
field: id
relationship: none
page_1:
display_plugin: page
id: page_1
......@@ -54,3 +53,24 @@ display:
options:
items_per_page: 2
page_2:
display_plugin: page
id: page_2
display_options:
defaults:
filters: false
cache: false
cache:
type: time
options:
results_lifespan: 3600
output_lifespan: 3600
filters:
test_cache_context:
id: test_cache_context
table: views_test_data
field: test_cache_context
relationship: none
cache_metadata:
contexts:
- views_test_cache_context
<?php
/**
* @file
* Contains \Drupal\views_test_data\Cache\ViewsTestCacheContext.
*/
namespace Drupal\views_test_data\Cache;
use Drupal\Core\Cache\CacheContextInterface;
/**
* Test cache context which uses a dynamic context coming from state.
*/
class ViewsTestCacheContext implements CacheContextInterface {
/**
* {@inheritdoc}
*/
public static function getLabel() {
return t('Views test cache context');
}
/**
* {@inheritdoc}
*/
public function getContext() {
return \Drupal::state()->get('views_test_cache_context', 'George');
}
}
<?php
/**
* @file
* Contains \Drupal\views_test_data\Plugin\views\filter\ViewsTestCacheContextFilter.
*/
namespace Drupal\views_test_data\Plugin\views\filter;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
/**
* @ViewsFilter("views_test_test_cache_context")
*/
class ViewsTestCacheContextFilter extends FilterPluginBase {
/**
* {@inheritdoc}
*/
public function query() {
$this->value = \Drupal::state()->get('views_test_cache_context', 'George');
parent::query();
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
$cache_contexts = parent::getCacheContexts();
$cache_contexts[] = 'views_test_cache_context';
return $cache_contexts;
}
}
services:
cache_context.views_test_cache_context:
class: Drupal\views_test_data\Cache\ViewsTestCacheContext
tags:
- { name: cache.context }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment