Так уж вышло, что мне нужно по одному и тому же URL генерировать немножко разные страницы в зависимости от наличия/отсутствия некоей куки. В результате пришлось отключить Internal Page Cache, так как закэшится первый запрошенный вариант страницы и будет далее выдаваться всем независимо от куки. Сайт по мере роста базы данных стал ощутимо тормозить, и очень хочется всё же найти способ задействовать страничный кэш.
Наткнулся на такую статью в которой предлагается простой способ модифицировать ядерный механизм кэширования, добавив нужную куку в Cache ID, то есть страница с кукой и страница без куки будут считаться разными страницами и соответственно кэшироваться раздельно, что мне и нужно.
Всё прочитал и понял, кроме последнего пассажа:
And the last thing is to add the cache context to your cache array
$build['#cache']['contexts' ][] = 'cookies:my_cookie_name';
Вопрос: КУДА этот код добавить-то? Наверняка ответ очевиден, но я туплю, а автор статьи не торопится отвечать.
Заранее премного.
Комментарии
Ключевое слово тут
$build
. Т.е. этот элемент добавляется просто к любому возвращаемому из какого-то контроллера (страницы или формы) рендер-массиву.Для контроллеров страниц возвращаемый массив обычно именуют
$build
, для форм -$form
. В данном примере, судя по всему, контроллер страницы.Собственно, ключ кеширования может применяться не только к верхнему уровню рендер-массива (например формы -
$form
), но индивидуально (и независимо от общего ключа) к отдельным её элементам, например, какому-то полю:<?php
$form["boxberry_address"]["#cache"] = [
"contexts" => [],
"tags" => [],
"max-age" => 0, // Для примера отключаем кеширование только элемента "boxberry_address" в текущей форме.
];
?>
Т.е. тут вопрос в том, что именно хотите кешировать в выводимых данных.
Насколько я понимаю, Internal Page Cache кэширует целиком страницу. То есть в самом начале работы контроллера страницы нужно проверить нет ли уже в кэше страницы с этим адресом И ЭТОЙ КУКОЙ, и если есть, то просто отдать из кэша. А если нет, и дело дошло до рендеринга страницы, то после рендеринга нужно положить свежеотрендеренную страницу в кэш, причем с id, включающим куку.
Пока писал, осознал, что это ж у меня в БД будут храниться десятки тысяч готовых страниц. Может не зря Друпал рекомендует Internal Page Cache for small to medium-sized websites? Может всё же Varnish освоить? Там поддержка кук из коробки есть.
Вот как раз для того, чтобы этим не заниматься и служит контекст кеша. Достаточно сообщить рендер-массиву зависимость контекста (в вашем случае от кук, хотя она может быть и от ID сущностей или ID термина или от ещё каких-то иных уникальных значений) и система (точнее, классы в обвязке сервиса
\Drupal::renderer()
) сама привяжет отображение к уникальному вхождению кеша на основании этой зависимости.На самом деле не совсем страниц, а результатов рендеринга build-массивов. Т.е., например, только отрендеренное тело ноды. Но вообще - да, каждый указанный ключ контекста (а он может быть не по одному параметру, как в вашем случае) добавляет в кеш свою группу к общему набору ключей контекста (или только их пересечениями/совокупностям). Т.е. если содержимое ноды чувствительно и к кукам и к параметрам, например, GET, и вы указали эту зависимость в кеш-контексте, то в конечном итоге будут построены вхождения для всех трёх случаев (по мере их рендеринга): для уникальных кук, для уникального параметра GET и для уникальных связок куки + GET.
Грубое объяснение, конечно, но примерно показывает картину.
PS. Опять же, мы рассматриваем типичный случай, когда build-массив представляет содержимое, отдаваемое контроллером. Но всё вышенаписанное справедливо и для любых других рендер-массивов, например, блоков.
Есть только один нюанс: документация по cache contexts в явном виде говорит:
То есть Internal Page Cache (не путать с Dynamic Page Cache, который у меня включён и что-то не особо ускоряет) вообще игнорирует контексты. Тогда зачем их добавлять в билд-массив? Весь остальной код в статье модифицирует функциональность именно Internal Page Cache (page_cache).
Я сейчас смутно припоминаю: году так в 2016, когда D8 был свеж, как утренняя булочка из пекарни, мы вроде как парились на одном проекте с аналогичным вопросом. Вроде что-то кешировалось неуместным образом для гостей в форме на фронте. Не помню точно, как решилось тогда, но кажется, действительно выключили модуль Internal Page Cache.
Если вернуться к исходной причине ваших поисков:
Для анонимов можно просто установить max-age => 0 в параметрах кеша билд-массива, это должно работать и с включенным Internal Page Cache, если не ошибаюсь. Правда, для авторизованных кеш тоже отключится. Но тогда можно попробовать сообщать разные параметры кеширования для массива в зависимости от признака: авторизован или нет. Просто как первая мысль.
Но в целом - кеширование по каким-то наиболее распространённым ключам контекста (а-ля uid) всё же более обосновано и имеет бОльшее значение именно для режима авторизованного пользователя. Как гарантия, что конфиденциальные значения/данные не попадут в общий (т.е. разделяемый) кеш. Потом, не забывайте про админку - там тоже нужен кеш на нагруженных страницах (представления, списки и т.д.). И опять же - зависимый от роли пользователя.