I have run into an issue where a block that should be unique per page isn't for logged-out users. The issue is a custom block plugin I have on a views search page that contains custom filters (sort of like a custom replacement for exposed filters. the block placed through /admin/structure/block).
Based on what I have learned about Drupal 8, I added the cache contexts to my build array:
public function build() {
$search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
return [
'search_form' => $search_form,
'#cache' => ['contexts' => ['url.path', 'url.query_args']]
];
}
But it seems this must be incorrect because when logged-out, the block would get cached on the first view, and when the url changed, it wasn't showing a new version of the block.
I thought it might be the view page that was causing the issue, but even when I turned caching off on the view page, the issue remained.
I was able to fix the issue several ways, for example, by using a preprocess_block hook:
function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
$variables['#cache']['contexts'][] = 'url.path';
$variables['#cache']['contexts'][] = 'url.query_args';
}
But it bothered me I couldn't just put the cache contexts into my block's build array.
Since my block extends BlockBase, I decided to try out the getCacheContexts() method, especially since I saw some modules within core are doing it this way.
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
}
This is also fixed the issue, but interestingly, when I output the variables in the preprocess block function, these are not showing in $variables['#cache']['contexts'], but they do show in $variables['elements']['#cache']['contexts']
array:5 [▼
0 => "languages:language_interface"
1 => "theme"
2 => "url.path"
3 => "url.query_args"
4 => "user.permissions"
]
I'm trying to figure out how this works, and why it wasn't working from the build function.
Looking at /core/modules/block/src/BlockViewBuilder.php at the viewMultiple() function, it looks like it pulls the cache tags from the entity and the plugin:
'contexts' => Cache::mergeContexts(
$entity->getCacheContexts(),
$plugin->getCacheContexts()
),
So that explains why adding a getCacheContexts() method to my block plugin adds the contexts to my block. Also, looking at the preRender method in the same class, it looks like it doesn't use the cache array in the blocks build function, which confuses me, as it seems the way to add caching in Drupal 8 is to add a #cache element to render elements.
So my question is,
1) Are cache contexts added directly on the array in a block plugin ignored?
2) If so, is there a way around that, do we need to add it to a child element of the build array?
3) If context added directly is ignored, is adding a getCacheContexts() the way to go for block plugins in custom modules?