diff --git a/core/modules/rest/src/Plugin/views/display/RestExport.php b/core/modules/rest/src/Plugin/views/display/RestExport.php index 006871c..04ef341 100644 --- a/core/modules/rest/src/Plugin/views/display/RestExport.php +++ b/core/modules/rest/src/Plugin/views/display/RestExport.php @@ -11,9 +11,10 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableResponse; +use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Render\RendererInterface; use Drupal\Core\State\StateInterface; use Drupal\Core\Routing\RouteProviderInterface; -use Drupal\Tests\Core\Cache\CacheTagsInvalidatorTest; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\PathPluginBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -76,6 +77,13 @@ class RestExport extends PathPluginBase { protected $mimeType; /** + * The renderer + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** * Constructs a Drupal\rest\Plugin\ResourceBase object. * * @param array $configuration @@ -88,9 +96,13 @@ class RestExport extends PathPluginBase { * The route provider * @param \Drupal\Core\State\StateInterface $state * The state key value store. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteProviderInterface $route_provider, StateInterface $state, RendererInterface $renderer) { parent::__construct($configuration, $plugin_id, $plugin_definition, $route_provider, $state); + + $this->renderer = $renderer; } /** @@ -102,7 +114,8 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('router.route_provider'), - $container->get('state') + $container->get('state'), + $container->get('renderer') ); } @@ -267,14 +280,9 @@ public function execute() { $header = []; $header['Content-type'] = $this->getMimeType(); + $cache_metadata = CacheableMetadata::createFromRenderArray($output); $response = new CacheableResponse($this->renderer->renderRoot($output), 200); - $cache_metadata = new CacheableMetadata(); - - /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ - $cache = $this->getPlugin('cache'); - $cache_metadata->setCacheTags($cache->getCacheTags()); - $cache_metadata->setCacheMaxAge($cache->getCacheMaxAge()); $response->addCacheableDependency($cache_metadata); return $response; @@ -294,6 +302,20 @@ public function render() { $build['#suffix'] = ''; } + $build += ['#cache' => []]; + $build['#cache'] += [ + 'tags' => [], + 'max-age' => CacheBackendInterface::CACHE_PERMANENT, + ]; + + /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */ + $cache = $this->getPlugin('cache'); + + // If the output is a render array, add cache tags, regardless of whether + // caching is enabled or not; cache tags must always be set. + $build['#cache']['tags'] = Cache::mergeTags($build['#cache']['tags'], $cache->getCacheTags()); + $build['#cache']['max-age'] = Cache::mergeMaxAges($build['#cache']['max-age'], $cache->getCacheMaxAge()); + return $build; } diff --git a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php index fd01c78..a8b2dee 100644 --- a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php @@ -9,7 +9,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Cache\Cache; -use Drupal\page_cache\Tests\PageCacheTagsIntegrationTest; use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait; use Drupal\views\Views; use Drupal\views\Tests\Plugin\PluginTestBase; @@ -80,6 +79,8 @@ public function testSerializerResponses() { $actual_json = $this->drupalGet('test/serialize/field', array(), array('Accept: application/json')); $this->assertResponse(200); $this->assertCacheTags($view->getCacheTags()); + // @todo Due to https://www.drupal.org/node/2352009 we can't yet test the + // propagation of cache tags. // Test the http Content-type. $headers = $this->drupalGetHeaders(); @@ -145,7 +146,7 @@ public function testSerializerResponses() { /** * Tests the response format configuration. */ - public function ptestReponseFormatConfiguration() { + public function testReponseFormatConfiguration() { $this->drupalLogin($this->adminUser); $style_options = 'admin/structure/views/nojs/display/test_serializer_display_field/rest_export_1/style_options'; @@ -188,7 +189,7 @@ public function ptestReponseFormatConfiguration() { /** * Test the field ID alias functionality of the DataFieldRow plugin. */ - public function ptestUIFieldAlias() { + public function testUIFieldAlias() { $this->drupalLogin($this->adminUser); // Test the UI settings for adding field ID aliases. @@ -255,7 +256,7 @@ public function ptestUIFieldAlias() { /** * Tests the raw output options for row field rendering. */ - public function ptestFieldRawOutput() { + public function testFieldRawOutput() { $this->drupalLogin($this->adminUser); // Test the UI settings for adding field ID aliases. @@ -281,7 +282,7 @@ public function ptestFieldRawOutput() { /** * Tests the live preview output for json output. */ - public function ptestLivePreview() { + public function testLivePreview() { // We set up a request so it looks like an request in the live preview. $request = new Request(); $request->setFormat('drupal_ajax', 'application/vnd.drupal-ajax'); @@ -314,7 +315,7 @@ public function ptestLivePreview() { /** * Tests the views interface for rest export displays. */ - public function ptestSerializerViewsUI() { + public function testSerializerViewsUI() { $this->drupalLogin($this->adminUser); // Click the "Update preview button". $this->drupalPostForm('admin/structure/views/view/test_serializer_display_field/edit/rest_export_1', $edit = array(), t('Update preview')); @@ -327,7 +328,7 @@ public function ptestSerializerViewsUI() { /** * Tests the field row style using fieldapi fields. */ - public function ptestFieldapiField() { + public function testFieldapiField() { $this->drupalCreateContentType(array('type' => 'page')); $node = $this->drupalCreateNode(); diff --git a/core/modules/views/src/Plugin/views/cache/Tag.php b/core/modules/views/src/Plugin/views/cache/Tag.php index 20d97c3..87ac8ba 100644 --- a/core/modules/views/src/Plugin/views/cache/Tag.php +++ b/core/modules/views/src/Plugin/views/cache/Tag.php @@ -6,6 +6,7 @@ */ namespace Drupal\views\Plugin\views\cache; + use Drupal\Core\Cache\CacheBackendInterface; /** diff --git a/core/modules/views/src/Plugin/views/cache/Time.php b/core/modules/views/src/Plugin/views/cache/Time.php index f8ef786..37c6cdd 100644 --- a/core/modules/views/src/Plugin/views/cache/Time.php +++ b/core/modules/views/src/Plugin/views/cache/Time.php @@ -191,6 +191,8 @@ protected function cacheSetExpire($type) { * {@inheritdoc} */ protected function getDefaultCacheMaxAge() { + // The max age, unless overridden by some other piece of the rendered code + // is determined by the output time setting. return $this->cacheSetExpire('output'); } diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php index f751a63..03a7f37 100644 --- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php +++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php @@ -342,6 +342,9 @@ public function getCacheTags() { return []; } + /** + * {@inheritdoc} + */ public function getCacheMaxAges() { return Cache::PERMANENT; } diff --git a/core/modules/views/src/Tests/Plugin/CacheWebTest.php b/core/modules/views/src/Tests/Plugin/CacheWebTest.php index b3a01e2..676faa3 100644 --- a/core/modules/views/src/Tests/Plugin/CacheWebTest.php +++ b/core/modules/views/src/Tests/Plugin/CacheWebTest.php @@ -66,12 +66,18 @@ public function testCacheOutputOnPage() { $this->drupalGet('test-display'); $this->assertResponse(200); $this->assertTrue(\Drupal::cache('render')->get($output_key)); - $this->assertCacheMaxAge(time() + 3600); + $cache_tags = [ + 'config:user.role.anonymous', + 'config:views.view.test_display', + 'node_list', + 'rendered' + ]; + $this->assertCacheTags($cache_tags); $this->drupalGet('test-display'); $this->assertResponse(200); $this->assertTrue(\Drupal::cache('render')->get($output_key)); - $this->assertCacheMaxAge(time() + 3600); + $this->assertCacheTags($cache_tags); } } diff --git a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php index 9c8846a..77b9eaf 100644 --- a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php @@ -23,45 +23,42 @@ class SqlTest extends UnitTestCase { * @covers ::getAllEntities */ public function testGetCacheTags() { - $view = $this->getMockBuilder('Drupal\views\ViewExecutable') - ->disableOriginalConstructor() - ->getMock(); + $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal(); $query = new Sql([], 'sql', []); $query->view = $view; - $view->result = []; + $result = []; + $view->result = $result; // Add a row with an entity. $row = new ResultRow(); - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheTags') - ->willReturn(['entity_test:123']); - + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:123']); + $entity = $prophecy->reveal(); $row->_entity = $entity; - $view->result[] = $row; + + $result[] = $row; + $view->result = $result; // Add a row with an entity and a relationship entity. $row = new ResultRow(); - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheTags') - ->willReturn(['entity_test:124']); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:124']); + $entity = $prophecy->reveal(); $row->_entity = $entity; - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheTags') - ->willReturn(['entity_test:125']); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:125']); + $entity = $prophecy->reveal(); $row->_relationship_entities[] = $entity; - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheTags') - ->willReturn(['entity_test:126']); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:126']); + $entity = $prophecy->reveal(); $row->_relationship_entities[] = $entity; - $view->result[] = $row; + $result[] = $row; + $view->result = $result; $this->assertEquals(['entity_test:123', 'entity_test:124', 'entity_test:125', 'entity_test:126'], $query->getCacheTags()); } @@ -71,9 +68,7 @@ public function testGetCacheTags() { * @covers ::getAllEntities */ public function testGetCacheMaxAge() { - $view = $this->getMockBuilder('Drupal\views\ViewExecutable') - ->disableOriginalConstructor() - ->getMock(); + $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal(); $query = new Sql([], 'sql', []); $query->view = $view; @@ -82,31 +77,27 @@ public function testGetCacheMaxAge() { // Add a row with an entity. $row = new ResultRow(); - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheMaxAge') - ->willReturn(10); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(10); + $entity = $prophecy->reveal(); $row->_entity = $entity; $view->result[] = $row; // Add a row with an entity and a relationship entity. $row = new ResultRow(); - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheMaxAge') - ->willReturn(20); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(20); + $entity = $prophecy->reveal(); $row->_entity = $entity; - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheMaxAge') - ->willReturn(30); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(30); + $entity = $prophecy->reveal(); $row->_relationship_entities[] = $entity; - $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); - $entity->expects($this->any()) - ->method('getCacheMaxAge') - ->willReturn(40); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(40); + $entity = $prophecy->reveal(); $row->_relationship_entities[] = $entity; $this->assertEquals(10, $query->getCacheMaxAges());