MainContentViewSubscriber.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. <?php
  2. namespace Drupal\Core\EventSubscriber;
  3. use Drupal\Core\Cache\CacheableMetadata;
  4. use Drupal\Core\Cache\CacheableResponseInterface;
  5. use Drupal\Core\DependencyInjection\ClassResolverInterface;
  6. use Drupal\Core\Routing\RouteMatchInterface;
  7. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  8. use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
  9. use Symfony\Component\HttpKernel\KernelEvents;
  10. /**
  11. * View subscriber rendering main content render arrays into responses.
  12. *
  13. * Additional target rendering formats can be defined by adding another service
  14. * that implements \Drupal\Core\Render\MainContent\MainContentRendererInterface
  15. * and tagging it as a @code render.main_content_renderer @endcode, then
  16. * \Drupal\Core\Render\MainContent\MainContentRenderersPass will detect it and
  17. * use it when appropriate.
  18. *
  19. * @see \Drupal\Core\Render\MainContent\MainContentRendererInterface
  20. * @see \Drupal\Core\Render\MainContentControllerPass
  21. */
  22. class MainContentViewSubscriber implements EventSubscriberInterface {
  23. /**
  24. * The class resolver service.
  25. *
  26. * @var \Drupal\Core\Controller\ControllerResolverInterface
  27. */
  28. protected $classResolver;
  29. /**
  30. * The current route match.
  31. *
  32. * @var \Drupal\Core\Routing\RouteMatchInterface
  33. */
  34. protected $routeMatch;
  35. /**
  36. * The available main content renderer services, keyed per format.
  37. *
  38. * @var array
  39. */
  40. protected $mainContentRenderers;
  41. /**
  42. * URL query attribute to indicate the wrapper used to render a request.
  43. *
  44. * The wrapper format determines how the HTML is wrapped, for example in a
  45. * modal dialog.
  46. */
  47. const WRAPPER_FORMAT = '_wrapper_format';
  48. /**
  49. * Constructs a new MainContentViewSubscriber object.
  50. *
  51. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
  52. * The class resolver service.
  53. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  54. * The current route match.
  55. * @param array $main_content_renderers
  56. * The available main content renderer service IDs, keyed by format.
  57. */
  58. public function __construct(ClassResolverInterface $class_resolver, RouteMatchInterface $route_match, array $main_content_renderers) {
  59. $this->classResolver = $class_resolver;
  60. $this->routeMatch = $route_match;
  61. $this->mainContentRenderers = $main_content_renderers;
  62. }
  63. /**
  64. * Sets a response given a (main content) render array.
  65. *
  66. * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event
  67. * The event to process.
  68. */
  69. public function onViewRenderArray(GetResponseForControllerResultEvent $event) {
  70. $request = $event->getRequest();
  71. $result = $event->getControllerResult();
  72. // Render the controller result into a response if it's a render array.
  73. if (is_array($result) && ($request->query->has(static::WRAPPER_FORMAT) || $request->getRequestFormat() == 'html')) {
  74. $wrapper = $request->query->get(static::WRAPPER_FORMAT, 'html');
  75. // Fall back to HTML if the requested wrapper envelope is not available.
  76. $wrapper = isset($this->mainContentRenderers[$wrapper]) ? $wrapper : 'html';
  77. $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$wrapper]);
  78. $response = $renderer->renderResponse($result, $request, $this->routeMatch);
  79. // The main content render array is rendered into a different Response
  80. // object, depending on the specified wrapper format.
  81. if ($response instanceof CacheableResponseInterface) {
  82. $main_content_view_subscriber_cacheability = (new CacheableMetadata())->setCacheContexts(['url.query_args:' . static::WRAPPER_FORMAT]);
  83. $response->addCacheableDependency($main_content_view_subscriber_cacheability);
  84. }
  85. $event->setResponse($response);
  86. }
  87. }
  88. /**
  89. * {@inheritdoc}
  90. */
  91. public static function getSubscribedEvents() {
  92. $events[KernelEvents::VIEW][] = ['onViewRenderArray'];
  93. return $events;
  94. }
  95. }