function HtmxRenderer::renderResponse

We only wrap the necessary content into a full HTML document to be processed by HTMX on the frontend.

Parameters

array $main_content: The render array representing the main content.

\Symfony\Component\HttpFoundation\Request $request: The request object, for context.

\Drupal\Core\Routing\RouteMatchInterface $route_match: The route match, for context.

Return value

\Symfony\Component\HttpFoundation\Response The Response in the format that this implementation supports.

Overrides MainContentRendererInterface::renderResponse

File

core/lib/Drupal/Core/Render/MainContent/HtmxRenderer.php, line 55

Class

HtmxRenderer
Main content renderer for HTMX requests.

Namespace

Drupal\Core\Render\MainContent

Code

public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) {
  $token = Crypt::randomBytesBase64(55);
  $html = [
    '#type' => 'inline_template',
    // Add a noindex meta tag to make sure this response will not be indexed.
'#template' => <<<HTMX_RESPONSE
    <!doctype html>
    <html>
    <head>
    <meta name="robots" content="noindex">
    <title>{{ title }}</title>
    <css-placeholder token="{{ placeholder_token }}">
    <js-placeholder token="{{ placeholder_token }}">
    <js-bottom-placeholder token="{{ placeholder_token }}">
    </head>
    <body>{{ content }}</body>
    </html>
    HTMX_RESPONSE,
    '#context' => [
      'title' => $main_content['#title'] ?? $this->titleResolver
        ->getTitle($request, $route_match->getRouteObject()),
      'content' => [
        'messages' => [
          '#type' => 'status_messages',
        ],
        'main_content' => $main_content,
      ],
      'placeholder_token' => $token,
    ],
  ];
  // Create placeholder strings for these keys.
  // @see \Drupal\Core\Render\HtmlResponseSubscriber
  $types = [
    'styles' => 'css',
    'scripts' => 'js',
    'scripts_bottom' => 'js-bottom',
  ];
  foreach ($types as $type => $placeholder_name) {
    $placeholder = '<' . $placeholder_name . '-placeholder token="' . $token . '">';
    $html['#attached']['html_response_attachment_placeholders'][$type] = $placeholder;
  }
  // Render, but don't replace placeholders yet, because that happens later in
  // the render pipeline. To not replace placeholders yet, we use
  // RendererInterface::render() instead of RendererInterface::renderRoot().
  // @see \Drupal\Core\Render\HtmlResponseAttachmentsProcessor.
  $render_context = new RenderContext();
  $this->renderer
    ->executeInRenderContext($render_context, function () use (&$html) {
    // RendererInterface::render() renders the $html render array and updates
    // it in place. We don't care about the return value (which is just
    // $html['#markup']), but about the resulting render array.
    // @todo Simplify this when https://www.drupal.org/node/2495001 lands.
    $this->renderer
      ->render($html);
  });
  // RendererInterface::render() always causes bubbleable metadata to be
  // stored in the render context, no need to check it conditionally.
  $bubbleable_metadata = $render_context->pop();
  $bubbleable_metadata->applyTo($html);
  $content = $this->renderCache
    ->getCacheableRenderArray($html);
  // Also associate the required cache contexts.
  // (Because we use ::render() above and not ::renderRoot(), we manually must
  // ensure the HTML response varies by the required cache contexts.)
  $content['#cache']['contexts'] = Cache::mergeContexts($content['#cache']['contexts'], $this->rendererConfig['required_cache_contexts']);
  // Also associate the "rendered" cache tag. This allows us to invalidate the
  // entire render cache, regardless of the cache bin.
  $content['#cache']['tags'][] = 'rendered';
  $response = new HtmlResponse($content, 200, [
    'Content-Type' => 'text/html; charset=UTF-8',
    // Make sure bots do not show this response in search results.
'X-Robots-Tag' => 'noindex',
  ]);
  return $response;
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.