/** * Tests post-render cache-integrated 'render_cache_placeholder' child * element. */ function testDrupalRenderChildElementRenderCachePlaceholder() { $container = array('#type' => 'container'); $context = array('bar' => $this->randomContextValue()); $callback = 'common_test_post_render_cache_placeholder'; $placeholder = drupal_render_cache_generate_placeholder($callback, $context); $test_element = array('#post_render_cache' => array($callback => array($context)), '#markup' => $placeholder, '#prefix' => '<foo>', '#suffix' => '</foo>'); $container['test_element'] = $test_element; $expected_output = '<div><foo><bar>' . $context['bar'] . '</bar></foo></div>' . "\n"; // #cache disabled. drupal_static_reset('_drupal_add_js'); $element = $container; $output = drupal_render($element); $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); // The cache system is turned off for POST requests. $request_method = \Drupal::request()->getMethod(); \Drupal::request()->setMethod('GET'); // GET request: #cache enabled, cache miss. drupal_static_reset('_drupal_add_js'); $element = $container; $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET'); $element['test_element']['#cache'] = array('cid' => 'render_cache_placeholder_test_child_GET'); // Simulate element rendering in a template, where sub-items of a renderable // can be sent to drupal_render() before the parent. $child =& $element['test_element']; $element['#children'] = drupal_render($child, TRUE); // Eventually, drupal_render() gets called on the root element. $output = drupal_render($element); $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data for child element. $child_tokens = $element['test_element']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; $parent_tokens = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; $expected_token = $child_tokens; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></foo>', '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered' => TRUE))); $this->assertIdentical($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.'); // GET request: validate cached data (for the parent/entire render array). $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached parent element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the parent element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<div><foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></foo></div>' . "\n", '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered' => TRUE))); $this->assertIdentical($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.'); // GET request: validate cached data. // Check the cache of the child element again after the parent has been // rendered. $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Verify that the child element contains the correct // render_cache_placeholder markup. $expected_token = $child_tokens; $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<foo><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></foo>', '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered' => TRUE))); $this->assertIdentical($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.'); // GET request: #cache enabled, cache hit. drupal_static_reset('_drupal_add_js'); $element = $container; $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET'); // Simulate element rendering in a template, where sub-items of a renderable // can be sent to drupal_render before the parent. $child =& $element['test_element']; $element['#children'] = drupal_render($child, TRUE); $output = drupal_render($element); $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.'); $settings = $this->parseDrupalSettings(drupal_get_js()); $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.'); // Restore the previous request method. \Drupal::request()->setMethod($request_method); }
/** * Tests entity render cache with references. */ public function testEntityViewBuilderCacheWithReferences() { // Force a request via GET so we can get drupal_render() cache working. $request = \Drupal::request(); $request_method = $request->server->get('REQUEST_METHOD'); $request->setMethod('GET'); // Create an entity reference field and an entity that will be referenced. entity_reference_create_instance('entity_test', 'entity_test', 'reference_field', 'Reference', 'entity_test'); entity_get_display('entity_test', 'entity_test', 'full')->setComponent('reference_field')->save(); $entity_test_reference = $this->createTestEntity('entity_test'); $entity_test_reference->save(); // Get a fully built entity view render array for the referenced entity. $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full'); $cid_reference = drupal_render_cid_create($build); $bin_reference = $build['#cache']['bin']; // Mock the build array to not require the theme registry. unset($build['#theme']); $build['#markup'] = 'entity_render_test'; drupal_render($build); // Test that a cache entry was created for the referenced entity. $this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.'); // Create another entity that references the first one. $entity_test = $this->createTestEntity('entity_test'); $entity_test->reference_field->entity = $entity_test_reference; $entity_test->save(); // Get a fully built entity view render array. $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); $cid = drupal_render_cid_create($build); $bin = $build['#cache']['bin']; // Mock the build array to not require the theme registry. unset($build['#theme']); $build['#markup'] = 'entity_render_test'; drupal_render($build); // Test that a cache entry is created. $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); // Save the entity and verify that both cache entries have been deleted. $entity_test->save(); $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.'); $this->assertFalse($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render cache for the referenced entity has been cleared when the entity was deleted.'); // Restore the previous request method. $request->setMethod($request_method); }
/** * Tests block render cache handling with configurable cache contexts. * * This is only intended to test that an existing block can be configured with * additional contexts, not to test that each context works correctly. * * @see \Drupal\block\Tests\BlockCacheTest */ public function testBlockViewBuilderCacheContexts() { // Force a request via GET so we can get drupal_render() cache working. $request = \Drupal::request(); $request_method = $request->server->get('REQUEST_METHOD'); $request->setMethod('GET'); // First: no cache context. $this->setBlockCacheConfig(array('max_age' => 600)); $build = $this->getBlockRenderArray(); $cid = drupal_render_cid_create($build); drupal_render($build); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); // Second: the "per URL" cache context. $this->setBlockCacheConfig(array('max_age' => 600, 'contexts' => array('cache_context.url'))); $old_cid = $cid; $build = $this->getBlockRenderArray(); $cid = drupal_render_cid_create($build); drupal_render($build); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); $this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.'); // Third: the same block configuration, but a different URL. $original_url_cache_context = $this->container->get('cache_context.url'); $request_stack = new RequestStack(); $request_stack->push(Request::create('/foo')); $temp_context = new UrlCacheContext($request_stack); $this->container->set('cache_context.url', $temp_context); $old_cid = $cid; $build = $this->getBlockRenderArray(); $cid = drupal_render_cid_create($build); drupal_render($build); $this->assertTrue($this->container->get('cache.render', $cid), 'The block render element has been cached.'); $this->assertNotEqual($cid, $old_cid, 'The cache ID has changed.'); $this->container->set('cache_context.url', $original_url_cache_context); // Restore the previous request method. $request->setMethod($request_method); }
/** * Tests child element that uses #post_render_cache but that is rendered via a * template. */ function testDrupalRenderChildElementRenderCachePlaceholder() { $context = array('bar' => $this->randomContextValue()); $callback = 'common_test_post_render_cache_placeholder'; $placeholder = drupal_render_cache_generate_placeholder($callback, $context); $test_element = ['#theme' => 'common_test_render_element', 'foo' => ['#post_render_cache' => [$callback => [$context]], '#markup' => $placeholder, '#prefix' => '<pre>', '#suffix' => '</pre>']]; $expected_output = '<pre><bar>' . $context['bar'] . '</bar></pre>' . "\n"; // #cache disabled. $element = $test_element; $output = drupal_render_root($element); $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output'); $expected_js = [['type' => 'setting', 'data' => ['common_test' => $context]]]; $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.'); // The cache system is turned off for POST requests. $request_method = \Drupal::request()->getMethod(); \Drupal::request()->setMethod('GET'); // GET request: #cache enabled, cache miss. $element = $test_element; $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET'); $element['foo']['#cache'] = array('cid' => 'render_cache_placeholder_test_child_GET'); // Render, which will use the common-test-render-element.html.twig template. $output = drupal_render_root($element); $this->assertIdentical($output, $expected_output); //, 'Placeholder was replaced in output'); $this->assertTrue(isset($element['#printed']), 'No cache hit'); $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.'); $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.'); // GET request: validate cached data for child element. $child_tokens = $element['foo']['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; $parent_tokens = $element['#post_render_cache']['common_test_post_render_cache_placeholder'][0]['token']; $expected_token = $child_tokens; $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<pre><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>', '#attached' => array(), '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered'))); $this->assertIdentical($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.'); // GET request: validate cached data (for the parent/entire render array). $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Parse unique token out of the cached markup. $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached parent element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the parent element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<pre><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>' . "\n", '#attached' => array(), '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered'))); $this->assertIdentical($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.'); // GET request: validate cached data. // Check the cache of the child element again after the parent has been // rendered. $element = array('#cache' => array('cid' => 'render_cache_placeholder_test_child_GET')); $cached_element = \Drupal::cache('render')->get(drupal_render_cid_create($element))->data; // Verify that the child element contains the correct // render_cache_placeholder markup. $expected_token = $child_tokens; $dom = Html::load($cached_element['#markup']); $xpath = new \DOMXPath($dom); $nodes = $xpath->query('//*[@token]'); $this->assertTrue($nodes->length, 'The token attribute was found in the cached child element markup'); $token = ''; if ($nodes->length) { $token = $nodes->item(0)->getAttribute('token'); } $this->assertIdentical($token, $expected_token, 'The tokens are identical for the child element'); // Verify the token is in the cached element. $expected_element = array('#markup' => '<pre><drupal-render-cache-placeholder callback="common_test_post_render_cache_placeholder" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>', '#attached' => array(), '#post_render_cache' => array('common_test_post_render_cache_placeholder' => array($context)), '#cache' => array('tags' => array('rendered'))); $this->assertIdentical($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.'); // GET request: #cache enabled, cache hit. $element = $test_element; $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET'); // Render, which will use the common-test-render-element.html.twig template. $output = drupal_render_root($element); $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output'); $this->assertFalse(isset($element['#printed']), 'Cache hit'); $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.'); $this->assertIdentical($element['#attached']['js'], $expected_js, '#attached is modified; JavaScript setting is added to page.'); // Restore the previous request method. \Drupal::request()->setMethod($request_method); }