Drupal preprocess хуки: почему контекст кеширования `url.path` имеет значение
Когда вы изменяете вывод в хуке предварительной обработки на основе текущего маршрута или пути, вам также необходимо сообщить кэшу рендеринга Drupal, что именно делает этот вывод вариативным. В противном случае первый отрендеренный результат может быть закэширован и использован повторно везде.
Ситуация
Рассмотрим пользовательскую предварительную обработку для блока брендинга, который отображает другое название сайта на узлах блога:
getRouteName();
// Проверяем, находимся ли мы на странице блога
if ($route_name === 'entity.node.canonical') {
$node = $route_match->getParameter('node');
if ($node && $node->bundle() === 'blog') {
// На страницах блога изменяем название сайта, чтобы отобразить "HELLO WORLD"
$variables['site_name'] = 'HELLO WORLD';
}
}
// На главной странице и других страницах сохраняем исходное название сайта
}
Без строки, добавляющей контекст кэша ($variables['#cache']['contexts'][] = 'url.path';
), Drupal может закэшировать первую отрендеренную версию (например, версию для блога) и использовать ее повторно на каждой странице. Вот почему вы видели один и тот же текст везде.
Почему это происходит
- Кэширование рендеринга: Drupal кэширует массивы рендеринга для повышения производительности.
- Вариативный вывод: Если вывод изменяется в зависимости от пути или маршрута, ключ кэша должен включать эту вариативность.
- Контексты кэша: Добавление
url.path
(или более специфичного контекста) сообщает Drupal о необходимости поддерживать отдельные вариации кэша для каждого пути.
Распространяется ли это на все хуки предварительной обработки?
Да, правило применяется к любому рендерируемому выводу, который является вариативным. Хуки предварительной обработки часто изменяют массивы рендеринга или переменные шаблона, которые становятся частью кэша рендеринга. Если ваша логика зависит от:
- Пути или маршрута → добавьте
url.path
илиroute
- Текущего пользователя → добавьте
user
(или более гранулярные контексты, такие какuser.roles
) - Языка → добавьте
languages:language_interface
- Темы или точки останова → добавьте
theme
,responsive_image_style
и т. д.
Сама предварительная обработка не является чем-то особенным; важно то, должен ли результирующий массив рендеринга кэшироваться по-разному между запросами. Если вывод вариативен, объявите правильные контексты кэша.
Выбор правильного контекста кэша
- Предпочитайте точность: Если вы полагаетесь на канонический узел, рассмотрите
route
вместоurl.path
, чтобы избежать ненужной фрагментации кэша (например, из-за строк запроса или псевдонимов). - Блоки, управляемые сущностями: Когда вы полагаетесь на сущность (например, узел), добавьте зависимости кэша, чтобы правки корректно очищались:
$variables['#cache']['tags'][] = 'node:' . $node->id();
- Max-age: Оставляйте
max-age
по умолчанию (бессрочно), если вывод не имеет истинного срока действия.
Более безопасная альтернатива: получение данных из массива рендеринга
Вместо использования глобальных переменных вы также можете прикрепить метаданные кэшируемости с помощью CacheableMetadata
для безопасного объединения контекстов и тегов:
addCacheContexts(['route']);
$route_match = \Drupal::routeMatch();
if ($route_match->getRouteName() === 'entity.node.canonical') {
$node = $route_match->getParameter('node');
if ($node && $node->bundle() === 'blog') {
$variables['site_name'] = 'HELLO WORLD';
$cacheable->addCacheTags(['node:' . $node->id()]);
}
}
$cacheable->applyTo($variables);
}
Контрольный список
- Изменяется ли вывод в зависимости от пути или маршрута? Добавьте
url.path
илиroute
. - Зависит ли он от сущности? Добавьте теги кэша для этой сущности.
- Изменяется ли он в зависимости от пользователя или языка? Добавьте соответствующие контексты.
- Поддерживайте высокое значение
max-age
; полагайтесь на теги для инвалидации.
Вывод
Да — если ваша логика предварительной обработки изменяет вывод для каждой страницы, вы должны объявить соответствующие контексты кэша. В противном случае Drupal будет с удовольствием выдавать один и тот же закэшированный результат везде.