3

I have a controller action that renders a list nodes and returns them, and only them. It's invoked via AJAX and the JS code just takes the result and inserts it into the page somewhere.

My problem is getting the cache metadata from the render array of nodes and passing it to the response. Here's the simplified action:

public function myControllerAction() {
  $builder = \Drupal::entityTypeManager()->getViewBuilder('node');
  // Get render array containing a few nodes.
  $nodes = $builder->viewMultiple([1,2,3], 'teaser');

  $response = new CacheableResponse();
  $response->setCacheableMetadata(CacheableMetadata::createFromRenderArray($nodes));
  return $response;
}

The problem is CacheableMetadata::createFromRenderArray() expects you to not give it a nested render array, but that's what the view builder returns. It returns an array of render arrays.

How can I efficiently extract the metadata from each node in the list and apply it to the response?

1
  • Maybe just loop and merge?
    – Clive
    Sep 12, 2017 at 18:37

1 Answer 1

4

Render the nodes in a render context, so that the metadata can bubble up:

  $storage = \Drupal::entityTypeManager()->getStorage('node');
  $builder = \Drupal::entityTypeManager()->getViewBuilder('node');
  $nodes = $storage->loadMultiple([1,2,3]);
  $build = $builder->viewMultiple($nodes, 'teaser');

  $render_context = new \Drupal\Core\Render\RenderContext();
  \Drupal::service('renderer')->executeInRenderContext($render_context, function() use (&$build) {
    \Drupal::service('renderer')->render($build, TRUE);
  });

  $response = new CacheableResponse();
  if (!$render_context->isEmpty()) {
    $response->addCacheableDependency($render_context->pop());
  }

The rendering is not lost. The rendered nodes are in $build['#markup'] and you can add the html to the response if that is what you want to do:

$response->setContent($build['#markup']);

Edit:

Just seen in the related question on the right a similar topic. While the above code is good for demonstration purpose, because it shows how to get cache metadata from a nested render array, the simpler code in the answer to How to handle cache metadata in an AJAX response? is actually working for this nested build array too, because the dependencies of the build array are added after rendering and at this point the metadata has bubbled up to the top level.

1
  • Thank you! That other question is from me as well, from a different project a while ago. Your edit to this answer made me realize why that original method wasn't working for me this time around - I was adding the cache metadata from the render array before I called renderRoot on it. Now it works. Also it's great to get a good example of a render context here. Thanks again!
    – Brian
    Sep 13, 2017 at 13:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.