Score:0

EventSubscriber to add cache context for specific path

cn flag

I am working with Mobile Detect module and from what I understand, we need to manually add the cache context mobile_detect_is_mobile in order to have the page cached differently based on the user device type. I was able to make it work by applying the cache context using a preprocess on my paragraph like so :

function hook_preprocess_paragraph(&$variables){
    $variables['#cache']['contexts'][] = 'mobile_detect_is_mobile';
}

Although, I don't like the idea of adding the context at the preprocess level and I was looking to have a more global solution by using an EventSubscriber that checks the path and apply the cache context for specific paths. It seems like the cache context is not taken in consideration. Here my EventSubscriber :

class MobileDetectSubscriber implements EventSubscriberInterface {

    /**
     * @var \Symfony\Component\HttpFoundation\Request
     */
    protected $request;

    /**
     * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
     */
    public function __construct(RequestStack $request_stack) {
    $this->request = $request_stack->getCurrentRequest();;
    }

    /**
     * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
     *   The event to process.
     */
    public function onRespond(FilterResponseEvent $event) {
        if (!$event
            ->isMasterRequest()) {
            return;
        }
        $response = $event
            ->getResponse();
        if (!$response instanceof CacheableResponseInterface) {
            return;
        }

        if($this->request->getRequestUri() == "/"){ 
            $mobile_detect = new CacheableMetadata();
            $mobile_detect->setCacheContexts(['mobile_detect_is_mobile']);
            $response->addCacheableDependency($mobile_detect);
        }
    }

    /**
     * @return array
     *   An array of event listener definitions.
     */
    public static function getSubscribedEvents() {
        // Priority 5, so that it runs before FinishResponseSubscriber, but after
        // event subscribers that add the associated cacheability metadata (which
        // have priority 10). This one is conditional, so must run after those.
        $events[KernelEvents::RESPONSE][] = ['onRespond', 5];
        return $events;
    }
}

What am I missing ?

miststudent2011 avatar
fr flag
Have you written services.yml as well?
Score:2
cn flag

Adding the cache context to the response is not more global. It's quite the opposite. Only if you put the cache context in the same render element which depends on this context it can be merged into all caching levels affected. In this example the context of the paragraph will be merged with the parent node and then with the page. The paragraph and node could also be in a block or a view. All these elements need that cache context.

Adding the cache context in a preprocess hook is indeed the last resort in the process of rendering a template. There is extra code in Drupal checking the top of $variables of a preprocess hook and merging the cache metadata into the template.

However, the idea behind cache metadata is to add it directly to the build array when generating content in code, so that it can bubble up to all templates involved. This works then also when refactoring the code to a function used in different places. For example a Twig function:

$build = [];
// build content
$build['#cache']['contexts'][] = 'mobile_detect_is_mobile';
// either return the build array to be rendered in {{ }}
return $build;
// or if your Twig function doesn't return $build bubble up at least the metadata 
// by rendering it inside of the Twig function
\Drupal::service('renderer')->render($build);
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.