Loading...
Loading...
Drupal cache contexts implementation guide. Use when asked about request-based cache variations, user.roles vs user context, URL contexts, language contexts, custom cache contexts, or cache context hierarchy. Helps prevent cache explosion from overly broad contexts.
npx skill4agent add sparkfabrik/sf-awesome-copilot drupal-cache-contextsVary| Context | Variations | Use Case |
|---|---|---|
| Per-user (AVOID) | Truly personalized content only |
| Per-role combination | Role-based visibility |
| Per-permission set | Permission-based content |
| Per-path | Path-dependent content |
| Per-parameter | Sort, filter, pagination |
| Per-language | Translated content |
| Per-theme | Theme-specific rendering |
| Per-session | Session data (triggers placeholder) |
user (AVOID - per-user cache explosion)
├── user.is_super_user (2 variations only)
├── user.roles (per role combination)
│ └── user.roles:editor (has specific role?)
└── user.permissions (per permission set)$build = [
'#markup' => $this->getRoleBasedContent(),
'#cache' => [
'contexts' => ['user.roles'],
],
];// WARNING: Creates cache entry per user - use lazy_builder instead
$build = [
'#markup' => $user->getDisplayName(),
'#cache' => [
'contexts' => ['user'], // High cardinality!
],
];
// BETTER: Use lazy builder for per-user content
$build = [
'#lazy_builder' => ['my_module.lazy:userName', []],
'#create_placeholder' => TRUE,
];$sort = \Drupal::request()->query->get('sort', 'date');
$build = [
'#theme' => 'item_list',
'#items' => $this->getSortedItems($sort),
'#cache' => [
'contexts' => ['url.query_args:sort'],
],
];$build = [
'#markup' => $this->t('Welcome'),
'#cache' => [
'contexts' => ['languages:language_interface'],
],
];$build = [
'#markup' => $this->getLocalizedRoleContent(),
'#cache' => [
'contexts' => [
'user.roles',
'languages:language_interface',
],
],
];# my_module.services.yml
services:
cache_context.custom_header:
class: Drupal\my_module\Cache\CustomHeaderContext
arguments: ['@request_stack']
tags:
- { name: cache.context }namespace Drupal\my_module\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
class CustomHeaderContext implements CacheContextInterface {
public static function getLabel() {
return t('Custom header');
}
public function getContext() {
$request = $this->requestStack->getCurrentRequest();
return $request->headers->get('X-Custom-Header', 'default');
}
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}// Usage
$build['#cache']['contexts'][] = 'custom_header';class ActionBlock extends BlockBase {
public function build() {
$actions = [];
if (\Drupal::currentUser()->hasPermission('edit content')) {
$actions[] = 'Edit';
}
return ['#markup' => implode(', ', $actions)];
}
public function getCacheContexts() {
return Cache::mergeContexts(
parent::getCacheContexts(),
['user.permissions']
);
}
}| Mistake | Impact | Solution |
|---|---|---|
Using | Cache explosion (1 entry per user) | Use |
Using | Triggers auto-placeholder | Use lazy builder |
| Missing context | Same cached content for all variations | Add appropriate context |
| Too broad context | Unnecessary cache variations | Use most specific context |
# services.yml - default conditions
renderer.config:
auto_placeholder_conditions:
contexts:
- 'session'
- 'user'# Enable debug headers
$settings['http.response.debug_cacheability_headers'] = TRUE;
# Check applied contexts
curl -sI https://site.com/ | grep X-Drupal-Cache-Contexts
# Output: X-Drupal-Cache-Contexts: languages:language_interface theme url.path user.permissions