Назад к блогу

Drupal preprocess хуки: почему контекст кеширования `url.path` имеет значение

2025-09-068 минут чтения

Когда вы изменяете вывод в хуке предварительной обработки на основе текущего маршрута или пути, вам также необходимо сообщить кэшу рендеринга 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 будет с удовольствием выдавать один и тот же закэшированный результат везде.