В Drupal контроллеры чаще всего возвращают Renderable
-массивы. Такие массивы автоматически проходят через систему рендеринга и подставляются в базовый шаблон темы (page.html.twig
). Но иногда бывает нужно получить полный Response
с темизацией вручную — например, когда дополнительно требуется работать с заголовками, cookie или особыми параметрами ответа.
В этой статье разберём технику возврата страницы из контроллера, которая одновременно:
- использует основной шаблон сайта (page.html.twig);
- может установить cookie;
- позволяет при необходимости встроить кастомный контент и скрипты.
Пример: промежуточная страница с установкой cookie
Рассмотрим контроллер, в котором показывается промежуточная страница. На ней можно вывести сообщение, подключить библиотеку JS, а заодно установить cookie (например, выбранный город).
В маршрутах добавим путь к нашему контроллеру (mymodule.routing.yml):
1 2 3 4 5 6 7 8 9 |
# Промежуточный редирект с установкой куки mymodule.user_city_redirect: path: '/select-city/{node}/{city}' defaults: _controller: '\Drupal\mymodule\Controller\RedirectController::redirectGateway' requirements: _permission: 'access content' node: '\d+' city: '\d+' |
Передаётся два параметра — это node id статьи, куда будет сделан редирект, и код города, который будет установлен как cookie в response объекте.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
class RedirectController extends ControllerBase { /** * Промежуточный вывод страницы с установкой cookie. */ public function redirectGateway(int $node, int $city) { $nodeObject = $this->validateRedirectNode($node); $cityObject = $this->validateRedirectCity($city); if ($nodeObject && $cityObject) { // Собираем alias ноды. $path = '/node/' . $nodeObject->id(); $targetUrl = \Drupal::service('path_alias.manager')->getAliasByPath($path); // Готовим cookie (на 30 дней). $cookie = new Cookie( 'selected_city', // имя cookie $city, // значение strtotime('+30 days'), // срок действия '/', // путь null, // домен FALSE, // secure (TRUE при https) FALSE // httpOnly ); // Render-массив для вывода в теме. $build = [ '#type' => 'markup', '#markup' => ' <div class="container"> <div class="redirect-page"> <span class="redirect-rule" data-redirect="' . $targetUrl . '"></span> <h2>' . $this->t('Выбран город %s. Сейчас произойдёт переход...', ['%s' => $cityObject->getName()]) . '</h2> <div class="pict"></div> </div> </div>', '#attached' => [ 'library' => [ 'my_module/redirect_gateway', // своя библиотека с js ], ], ]; // Получаем сервис рендера основной страницы. $request = \Drupal::request(); $route_match = RouteMatch::createFromRequest($request); $renderer = \Drupal::service('main_content_renderer.html'); // Генерируем полноценный Response с темизацией. $response = $renderer->renderResponse($build, $request, $route_match); // Добавляем cookie в заголовки. $response->headers->setCookie($cookie); return $response; } throw new NotFoundHttpException(); } /** * Валидация ноды мероприятия */ private function validateRedirectNode(int $node): ?Node { $nodeObject = Node::load($node); if ($nodeObject /* какая-то валидация */) { return $nodeObject; } return NULL; } /** * Валидация терма города */ private function validateRedirectCity(int $city): ?Term { $cityObject = Term::load($city); if ($cityObject /* какая-то валидация */) { return $cityObject; } return NULL; } } |
Что здесь происходит
Здесь много кода, который не важен для примера, но для цельности картинки я его оставляю. Что важно:
- Готовим render-массив (
$build
), как обычно. Его содержимое будет вставлено в зону{{ page.content }}
основного шаблонаpage.html.twig
. Тут мы без проблем можем подключить какие то front-end библиотеки. - Вызываем сервис
main_content_renderer.html
. Он берёт render-массив и прогоняет его через систему темизации Drupal. В результате получается готовыйHtmlResponse
. - Устанавливаем cookie с помощью
$response->headers->setCookie($cookie)
. - Возвращаем
Response
из контроллера.
Таким образом, мы получаем полноценную страницу Drupal (с шапкой, футером и всеми блоками), но при этом имеем контроль над HTTP-заголовками.
Когда это полезно
- нужно отобразить промежуточную страницу (например, при редиректах);
- требуется выставить cookie или спец. заголовки;
- нужно работать с
Response
напрямую, но при этом сохранить стандартный шаблон Drupal.