2

We're having a problem with caching that is affecting anonymous users.

We have a form in a block that when the user fills out the form, they are able to download a PDF. We hide the block on pages where we don't want this PDF download form to appear. Ideally the block is hidden, if no PDF is available for a particular page, but the block is being cached and displayed regardless the of the actual page. This only is a problem for anonymous users. Logged in users do not have the problem of the seemingly cached PDF download blocks appearing on the wrong pages.

For example after clearing the cache, somebody first visits page A with a PDF available, then that PDF download form block is being cached and the cached version is also displayed for page B which does not have a PDF download.

Same vice versa, if you visit Page B first, than the block is being cached with it's attribute display:none, and it will not be displayed for Page A either Basically, we need the visibility to be re-evaluated on each page load.

We're fairly certain it's a cache issue as when we inspect the live rendered code we can see the block IDs being mixed up on the page. For example: we'll see a body-tag includes a class "node-164", it's node 164 and the Download the PDF includes a class "PDF-node-205", so it's the cached version for node 205, it also includes the filename as a CSS classname, it's clearly the block for a different page node.

We've tried disabling the page cache, dynamic cache, and the twig cache and we're still having the same problem. And again it is only happens for anonymous users, logged in users do not see these cached blocks appearing on the wrong pages.

Has anyone run into this or know of another caching method that Drupal may be using that we're not aware of?

2

If I understand correctly, it is okay for the block to be cached, but each path/URL can have its own "version" of the form, right? In that case, you need to inform Drupal that this block changes depending on the current path/URL. Drupal 8 introduced the concept of "cache contexts" to do that. The cache context defines the external factors by which the return value can vary.

Now that you know this, you'll notice that this question is pretty similar to yours.

As documented in the change record, blocks can define their cache context by overriding the getCacheContexts() method from the BlockCase class. Overriding that method would probably look like this in your block class:

/**
 * {@inheritdoc}
 */
public function getCacheContexts() {
  return Cache::mergeContexts(parent::getCacheContexts(), ['url.path']);
}

As you can see I'm using the url.path context here. A list of contexts provided by Drupal core can be found in the documentation.

  • +1 I was looking for a list of cache contexts used by Drupal, and I was not able to find it. – kiamlaluno May 31 '16 at 20:18
  • 2
    While this works, sounds like what you actually depend on is the node. Then I'd suggest to use a plugin contexts as shown in my answer in the already referenced question (drupal.stackexchange.com/a/199601/31), drupal will ensure the correct cache contexts automatically for you then. – Berdir May 31 '16 at 20:25
0

The quickest and most effective way in this case is to disable caching for the block by setting max-age to zero:

$build['#cache']['max-age'] = 0;

There is no point in defining a cache context in this case, because the cached blocks for every url will fill up the cache bin, but will never be used.

Anonymous users are served from the page cache, which will cache for every url a different page by default.

Your debugging efforts were not successful, because you were not able to switch off block caching. It is not enough to uninstall the caching modules. Core can cache blocks without this. You have to use a null cache backend.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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