Модуль page_cache использует http_middleware, чтобы зацепиться за объект request и произвести кеширование страницы. Работает он для анонимных пользователей. Хотелось бы расширить его возможности и получить больше управления над кешированием.
Рассмотрим как переопределить зацепку page_cache, и изменить идентификатор cache_id (cid), который генерируется для страниц. Мы добавим зависимость от названия города, которое пользователь передаёт как cookie с именем city.
Чтобы до конца было ясно, что мы меняем, приведу пример записей из таблицы cache_page:

CID формируется как сумма схемы, хоста и uri. А также добавляется формат (html, json и т.п.). При этом в В Drupal 11 значение формата всегда устанавливается как NULL. Не знаю с чем это связано, нам это не мешает.
Посмотрим на определение http_middleware в page_cache (page_cache.services.yml)
1 2 3 4 5 6 7 8 9 |
services: ... http_middleware.page_cache: class: Drupal\page_cache\StackMiddleware\PageCache arguments: ['@cache.page', '@page_cache_request_policy', '@page_cache_response_policy'] tags: - { name: http_middleware, priority: 200, responder: true } |
Мы заменим здесь параметр class. Вместо него установим собственный класс (расширение данного), где переопределим функцию формирования cid.
Переопределение сервиса в Drupal
Так как под капотом у нас Symphony, то предполагается несколько возможных путей для переопределения. Я остановился на варианте с реализацией интерфейса ServiceModifierInterface.
Нам нужно создать файл /my_module/src/MyModuleServiceProvider.php, где реализуется указанный интерфейс. Также требуется реализовать ServiceProviderInterface, который требуется для всех сервисов. Но мы немного упростим задачу, и будем расширять класс ServiceProviderBase, который частично решает нашу задачу, и нам остаётся написать только один метод — alter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php namespace Drupal\my_module; use Drupal\my_module\StackMiddleware\CookiesPageCache; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceProviderBase; /** * Overrides the Core PageCache service. */ class MyModuleServiceProvider extends ServiceProviderBase { /** * Modifies existing service definitions. * * @param ContainerBuilder $container * The ContainerBuilder whose service definitions can be altered. */ public function alter(ContainerBuilder $container) { if ($container->hasDefinition('http_middleware.page_cache')) { $container ->getDefinition('http_middleware.page_cache') ->setClass(CookiesPageCache::class); } } } |
Тут мы проверяем наличие сервиса в контейнере друпал, и если он есть, то подменяем определение класса на наше собственное.
Переопределение генерации Cache ID
Теперь можно реализовать расширение класса PageCache.
/my_module/src/StackMiddleware/CookiesPageCache.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?php namespace Drupal\my_module\StackMiddleware; use Drupal\page_cache\StackMiddleware\PageCache; use Symfony\Component\HttpFoundation\Request; /** * Extends PageCache to include cookie value in the Cache ID. */ class CookiesPageCache extends PageCache { /** * @inheritdoc */ protected function getCacheId(Request $request) { if (!isset($this->cid)) { $cid_parts = [ $request->getSchemeAndHttpHost() . $request->getRequestUri(), $request->getRequestFormat(NULL), ]; // Only cache by Cookies on Node routes. $cid_parts[] = $request->cookies->get('city', 'any'); $this->cid = implode(':', $cid_parts); } return $this->cid; } } |
Результатом станет изменение cid, и мы получим разные кеши для разных значений переменной, переданной как cookie.

Этот механизм открывает много возможностей для развития идей заложенных в page_cache.