1

In my current project I need to alter node templates depending on previously visited landing page. My idea is that, when a visitor visits one of these landing pages, a cookie will be set, and subsequent node templates will be altered depending on that cookie.

I've got three view pages serving as entry points for my visitors:
myproject.com/tv
myproject.com/mobile
myproject.com/infopoint

In theme preprocess I'm checking/setting a cookie and assigning a template variable. The Twig templates do minor modifications depending on that variable:

function MYTHEME_preprocess(array &$variables, $hook) {
  $router = \Drupal::routeMatch();

  if ($route = $router->getRouteObject()) {
    $view_id = $route->getDefault('view_id');
    $display_id = $route->getDefault('display_id');
    $view_display_plugin = $route->getOption('_view_display_plugin_id');

    if ($view_id && $view_display_plugin === 'page') {
      if ($view_id == 'slide') {
        user_cookie_save(['device' => 'tv']);
        $variables['MYTHEME']['device'] = 'tv';
      }
      elseif ($view_id == 'tile' && $display_id == 'mobile') {
        user_cookie_save(['device' => 'mobile']);
        $variables['MYTHEME']['device'] = 'mobile';
      }
      elseif ($view_id == 'tile' && $display_id == 'infopoint') {
        user_cookie_save(['device' => 'infopoint']);
        $variables['MYTHEME']['device'] = 'infopoint';
      }
    }
  }

  // user_cookie_save prefixes all cookie names with 'Drupal.visitor.'
  if (isset($_COOKIE['Drupal.visitor.device']) && 
    in_array($_COOKIE['Drupal.visitor.device'], ['tv', 'mobile', 'infopoint'])) {
         // I'm doing design modifications in my Twig templates depending on this variable
        $variables['MYTHEME']['device'] = $_COOKIE['Drupal.visitor.device'];
    }
  }
}

In services.yml I've activated the cookies cache context

parameters:
  renderer.config:
    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions', 'url.site', 'cookies:Drupal.visitor.device']

In my Twig templates I've got multiple code pieces like this:

{% if MYTHEME.device == 'tv' %}
   <!-- code here -->
{% else %}
   <!-- other design here -->
{% endif %}

Sadly the code doesn't really work. On the entry pages, the device theme variable is always correct, but the cookie is not updated (seems as if only first uncached hit sets the cookie. I even have the weird situation that cookie and twig variable aren't equal.). On node templates the theme variable isn't set correctly at all.

Any ideas how to get the code above working?

PS: Due slow views with geo-distance calculations disabling cache is not an option, I need to provide cached pages also for anonymous users.

  • I need to provide cached pages also for anonymous users Then you'll need to turn off the Internal Page Cache module. Dynamic Page Cache can still be enabled, but the former is only for use in situations where exactly the same content gets served to every anonymous user – Clive Sep 10 '18 at 12:36
  • I thought providing a proper cache context saves me from disabling the Internal Page cache? However, I disabled the internal page cache, but it still doesn't fully solve the problems. It looks as if the cookie is "one step behind" (e.g. visiting mobile - infopoint - infpoint shows mobile - mobile - infopoint) – Hudri Sep 10 '18 at 12:48
  • No, internal page cache ignores cache contexts. Yes, there's no cookie on the first request (you haven't set it yet). Then you send it, and it comes back populated with the next request, assuming the UA sends it – Clive Sep 10 '18 at 12:52
4

I've updated the code in the question, the code is now working. It was almost complete, I had made some simple mistakes though:

  1. Even when providing a proper cache context, you still need to disable the Internal page cache module. Only Internal dynamic page cache module is allowed.
  2. user_cookie_save() prefixes the cookie name with Drupal.visitor. This is the real name you'll also see in your browser's dev console. But when checking for cookie names in PHP, dots and spaces are converted to underscores. That means using user_cookie_save(['mycookie' => 'some_value']); creates a cookie with a "real" name of Drupal.visitor.mycookie, but in PHP
    • your template/module code needs to check
      if ($_COOKIE['Drupal_visitor_mycookie'])
    • and you also have to use the underscore name in services.yml
      required_cache_contexts: ['cookies:Drupal_visitor_device', '...other_stuff...']
  3. Don't set the cookie value and check that updated cookie value in the same request:
    user_cookie_save(['mycookie' => 'new_value'])
    will immediatly update the cookie in the client browser, but PHP code in the same request like
    if ($_COOKIE['Drupal_visitor_mycookie'])
    will still return the original value from the previous request.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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