0

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 ?

1
  • Have you written services.yml as well? Oct 6, 2021 at 18:10

1 Answer 1

2

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);

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.