Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Кеш

Чтобы страницы открывались быстро, 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: она отключает кеш всей страницы.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.