Кеш
Чтобы страницы открывались быстро, Drupal сохраняет готовый вывод и при следующем запросе отдаёт его, не выполняя ваш код. Это и есть кеш. Модуль обязан сообщить, от чего зависит его вывод, — иначе посетители увидят устаревшие или чужие данные. Описание пишут прямо в массиве вывода, в свойстве #cache.
Три вопроса
| Свойство | На какой вопрос отвечает | Пример |
|---|---|---|
tags (метки) | От каких данных зависит вывод | node:5, node_list:article |
contexts (контексты) | Для кого и где вывод различается | user, user.roles, url.query_args |
max-age (срок жизни) | Сколько секунд вывод актуален | 3600, 0 |
Метки
Метка связывает вывод с данными. Когда данные меняются, Drupal сбрасывает всё, что помечено их меткой. Страница /blog зависит от списка статей:
// web/modules/custom/blog/src/Controller/BlogController.php
public function list(): array
{
// … запрос и загрузка статей из урока о запросах
$build = $this->entityTypeManager()
->getViewBuilder('node')
->viewMultiple($nodes, 'teaser');
$build['#cache']['tags'][] = 'node_list:article';
return $build;
}Метка node_list:article сбрасывается при создании, изменении и удалении любой статьи. Без неё новая статья не появилась бы на странице /blog, пока не очищен кеш. У каждой сущности есть и собственные метки: $node->getCacheTags() вернёт ['node:5'].
Контексты
Контекст говорит, что вывод бывает разным. Страница показывает имя пользователя — значит, для каждого пользователя нужен свой вариант:
// web/modules/custom/blog/src/Controller/BlogController.php
$build['hello'] = [
'#markup' => $this->t('Здравствуйте, @name!', ['@name' => $this->currentUser()->getDisplayName()]),
'#cache' => ['contexts' => ['user']],
];| Контекст | Вариант зависит от |
|---|---|
user | Конкретного пользователя |
user.roles | Набора ролей |
user.permissions | Набора прав |
url.path | Адреса страницы |
url.query_args | Параметров после знака вопроса |
languages | Языка |
Выбирайте самый общий подходящий: user.permissions даёт один вариант на всех с одинаковыми правами, а user — отдельный на каждого.
Срок жизни
// web/modules/custom/blog/src/Controller/BlogController.php
$build['rates'] = [
'#markup' => $this->t('Подписчиков: @count', ['@count' => $count]),
'#cache' => ['max-age' => 3600],
];Через час вывод построится заново. 0 означает «не кешировать». По умолчанию срок не ограничен — вывод живёт, пока не сброшена метка.
Свои данные в кеше
Кешировать можно не только вывод, но и результат долгой работы — например, ответ чужого сервиса:
// web/modules/custom/blog/src/Controller/BlogController.php
$cache = \Drupal::cache();
if ($cached = $cache->get('blog:stats')) {
$stats = $cached->data;
} else {
$stats = $this->loadStats();
$cache->set('blog:stats', $stats, time() + 3600, ['blog_stats']);
}get() возвращает объект, данные лежат в его свойстве data, или FALSE, если записи нет. Аргументы set(): имя записи, данные, время истечения и метки. loadStats() — ваш метод с долгим расчётом.
Сброс по метке
// в любом месте, например в submitForm формы подписки
use Drupal\Core\Cache\Cache;
Cache::invalidateTags(['blog_stats']);Строка сбрасывает всё, что помечено blog_stats: и запись выше, и любой вывод с этой меткой. Так кеш очищают точечно, когда изменились ваши собственные данные.
Частая ошибка
Блок выводит имя текущего пользователя, а все посетители видят имя того, кто открыл страницу первым. Причина — не указан контекст: Drupal сохранил один вариант на всех. Если вывод зависит от пользователя, роли, языка или параметра адреса, добавьте контекст; если от данных — их метки. И не лечите такие ошибки записью 'max-age' => 0: она отключает кеш всей страницы.