3

I need to set the user timezone on all pages in the JS drupalSettings object, so that it can be accessed on any page. As drupalSettings.user is provided by core, I want to set drupalSettings.user.timezone, with the user timezone from the server.

While this can quickly and easily be done in hook_page_attachments(), I am trying to switch from using hooks, to using Event Subscribers, as Drupal hooks are the old paradigm, and event subscribers is the new paradigm.

Can someone explain how to add attached assets (a library, or drupalSettings) to all pages from an event subscriber?

Additional Context

As mentioned by 4uk4 in the comments, this event is not dispatched by Drupal core, but rather by Symfony underneath the hood. This means it is not a true 'Drupal' methodology, as core maintainers have not chosen to dispatch an event that is equivalent to hook_page_attachments(). So which is better? The answer is 'neither' at this point, as core is ambivalent as to whether it invokes a hook or dispatches an event. It does however seem that core is moving towards creating events that match existing hooks, as can be seen in this issue: https://www.drupal.org/project/drupal/issues/2551893. And here is a discussion that considers the differences between hooks and events: https://www.previousnext.com.au/blog/alter-or-dispatch-drupal-8-events-versus-alter-hooks

5
  • 1
    Wouldn't that be the decision of the core maintainers to dispatch an event instead of invoking this hook?
    – 4uk4
    Oct 1, 2022 at 9:49
  • Core both dispatches the event, and invokes the hook. Traditionally I've implemented the hook, but I wanted to switch to using an event subscriber. Note that user timezone is not part of drupalSettings by default, I'm extending it.
    – Jaypan
    Oct 1, 2022 at 13:57
  • 1
    The hook is part of the Drupal API. You are replacing it with a Symfony API event. There are a lot of cases where you need to switch from one API to another, but avoiding hooks is not one of them.
    – 4uk4
    Oct 1, 2022 at 14:49
  • I'm trying to prioritize event subscribers over hooks, as they are proper OOP and can be extended or overridden as necessary. I'd be surprised if hooks weren't eventually removed altogether, as they are legacy.
    – Jaypan
    Oct 1, 2022 at 16:26
  • I added some additional context to the original question.
    – Jaypan
    Oct 1, 2022 at 16:38

1 Answer 1

2

This can be done by acting upon the Symfony\Component\HttpKernel\KernelEvents::RESPONSE event. See the code below for an example (Note - you'll need to register the event subscriber, which is not shown here):

namespace Drupal\[MODULE]\EventSubscriber;

use Drupal\Core\Render\AttachmentsInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Adds the user's timezone to the drupalSettings JS object on all pages.
 */
class SetUserTimezoneToDrupalSettings implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      // Respond to KernelEvents::RESPONSE.
      KernelEvents::RESPONSE => ['onResponse', 1000],
    ];
  }

  /**
   * Event handler for KernelEvents::RESPONSE.
   */
  public function onResponse(\Symfony\Component\HttpKernel\Event\ResponseEvent $event) {
    // Only act upon the master request and not sub-requests.
    if ($event->isMasterRequest()) {
      $response = $event->getResponse();
      // Only act if the response is one that is able to have attachments.
      if ($response instanceof AttachmentsInterface) {
        // Get any existing attachments.
        $attachments = $response->getAttachments();
        // Set the user's timezone to the drupalSettings array.
        $attachments['drupalSettings']['user']['timezone'] = date_default_timezone_get();
        // Set the updated array back on the object.
        $response->setAttachments($attachments);
      }
    }
  }

}
2
  • Asked and answered in the same minute. Amazing!
    – cilefen
    Oct 1, 2022 at 2:48
  • Thanks, it was actually a few hour process, I just figured that someone (very potentially myself) would be interested in this in the future, so after I figured out the solution, I posted the question with the answer at the same time, so people could reference it.
    – Jaypan
    Oct 1, 2022 at 13:59

Your Answer

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

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