3

In my node preprocess function I check the value of a query param...

$variables['exclude_urls'] = \Drupal::request()->get('exclude_urls');

And in my template, I have...

{% if not exclude_urls %}
  <div class="gr-url">{{ url }}</div>
{% endif %}

However, this approach doesn't work because the node preprocess function does not fire if nothing in the URL changes besides the value for the 'exclude_urls' param.

I have tried various methods to disable caching for the page. Adding this to the route doesn't work...

  options:
    no_cache: 'TRUE'

I also tried using the page_cache_kill_switch in my controller, but it doesn't work...

\Drupal::service("page_cache_kill_switch")->trigger();

Neither does adding the query parameter to cache contexts...

$element['#cache']['contexts'][] = 'url.query_args:exclude_urls';

Instead of using hook_preprocess_node, is there some other way I should be checking the value of $_GET['exclude_urls'] and passing that to the template? Do I add code to the twig template itself to check the value of 'exclude_urls'? Is there any sure way to force hook_preprocess_node to fire every time? What is the usual approach to conditioning display of content based on a URL query param so that caching won't interfere?

Update: To be clear, I have also tried the above approaches in combination, with no success. For example, the end of my page function in my controller looks like this...

\Drupal::service("page_cache_kill_switch")->trigger(); 
$element['#cache']['contexts'][] = 'url.query_args:exclude_urls';
return $element;

Update 2: In my local dev site, I do not experience this problem, and I've found that this line in my local settings makes it so hook_preprocess_node fires every time...

$settings['cache']['bins']['render'] = 'cache.backend.null';

However, I'm not sure how to take advantage of this in my production site, since I don't want to disable the render cache generally. I can do something like this in settings.php, but it's a hack:

$parts = explode('?', $_SERVER['REQUEST_URI'], 2);
if ('/disable/cache/at/this/path' == $parts[0]) {
  $settings['cache']['bins']['render'] = 'cache.backend.null';
}
4
  • You need solution 2 and 3 simultaneously - one to disable internal page cache, and one to vary the render cache. Have you tried them together?
    – Clive
    Mar 28, 2019 at 15:54
  • Thanks Clive. I added an update to the question to clarify that I have tried 2 and 3 in combination, with no success.
    – arnoldbird
    Mar 28, 2019 at 16:23
  • The internal page cache is no problem here because the cid is built from the entire url including the query string. The cache context url.query_args:exclude_urls is also correct. The only problem I see is the variable name used to store the cache context , in preprocess it is usually $variables, not $element. When the node has not the correct cache data you can disable page caching in all possible ways, this doesn't help.
    – 4uk4
    Mar 28, 2019 at 19:41
  • @4k4 The variable name for the array you return doesn't matter. It could be $my_silly_long_variable_name_with_underscores and it should still work.
    – arnoldbird
    Mar 29, 2019 at 15:00

1 Answer 1

3

There are three layers where rendered content can be cached:

  • The Page Cache, which stores complete pages based on the URL for anonymous users.
  • The Dynamic Page Cache, which stores pages with placeholders for dynamic content that is rendered on each request.
  • The Render Cache, which stores individual rendered elements.

Each has a corresponding cache bin that can be disabled during development in settings.php


To set an additional cache context in hook_preprocess, so that the Dynamic Page cache knows to re-render that fragment and the render cache stores variants as needed, update the #cache variable:

function hook_preprocess_HOOK(&$variables) {
  $variables['#cache']['contexts'][] = 'url.query_args:exclude_urls';
}
2
  • Better add the cache context to the node preprocess hook, so that the node varies already by this context. It then bubbles up to the page where this node is displayed.
    – 4uk4
    Mar 29, 2019 at 7:25
  • I added it in the node preprocess function, like 4k4 suggests, and it's working there. Thanks! This allows me to eliminate my hack.
    – arnoldbird
    Mar 29, 2019 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.