Score:0

Alter a controller's render array via KernelEvents::VIEW

jp flag

I'm trying to alter a contrib module controller's output via KernelEvents::VIEW. I want to display the output from the controller and append the markup returned from the event handler. This is the code I am using, but it doesn't work as expected.

class BuildAlter implements EventSubscriberInterface {

  public function alterBuild(ViewEvent $event) {
    $build = $event->getControllerResult();
    $build['content'] = [
      '#type' => 'markup',
      '#markup' => '<p>Added by ControllerAlterSubscriber.</p>'
    ];

    $event->setControllerResult($build);
  }

  public static function getSubscribedEvents() {
    return [
      // The VIEW event occurs when the return value of a controller
      // is not a Response instance.
      KernelEvents::VIEW => ['alterBuild', 50],
    ];
  }

}
4uk4 avatar
cn flag
You need to register the service in *.services.yml. See https://drupal.stackexchange.com/questions/281093/is-there-a-best-practice-for-overriding-a-contributed-modules-controller-in-d/281158#281158
Nicholas avatar
jp flag
The problem, the $build['content'] is not referenced in the main twig I wan to alter, see below how content is output in twig. `{{ last_checked }}` {% for project_type in project_types %} <h3>{{ project_type.label }}</h3> {{ project_type.table }} {% else %} <p>{{ no_updates_message }}</p> {% endfor %} ` Should I open an issue to output $build['content'] for that twig file, so other event listeners can alter/add the output via the 'content' key? I'm lost.
cn flag
_Should I open an issue_ I don't think that would be appropriate, there's nothing in convention or best practice to say a `'content'` variable has to be used for a Twig template. Perhaps you could think about altering the route instead and building your own view based on the original plus your changes?
4uk4 avatar
cn flag
The markup in `content` is its own render item. A variable would be `#content` if placed with a template `#theme`.
Nicholas avatar
jp flag
Thank you @Clive, will use this approach
apaderno avatar
us flag
The [`ViewEvent`](https://api.drupal.org/api/drupal/vendor%21symfony%21http-kernel%21Event%21ViewEvent.php/class/ViewEvent/9.2.x) class is only available on Drupal 9. On Drupal 8, the class to use is [`GetResponseForControllerResultEvent`](https://api.drupal.org/api/drupal/vendor%21symfony%21http-kernel%21Event%21GetResponseForControllerResultEvent.php/class/GetResponseForControllerResultEvent/9.2.x). Drupal core code doesn't help much in understanding how to implement that event handler. None of the event handlers implemented by Drupal core uses `setControllerResult()`.
apaderno avatar
us flag
Looking at the Drupal core code used for that type of event handler, I can only understand the event handler should not assume the value returned from `$event->getControllerResult()` is an array. `RenderArrayNonHtmlSubscriber::onRespond()` and `MainContentViewSubscriber::onViewRenderArray()` check the value returned is an array, and assume it's a render array. `PsrResponseSubscriber::onKernelView()` checks the value returned implements `ResponseInterface`.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.