cache_context
2019 30 Jul

Drupal 8 Cache Context: An efficient way for context based caching.

We are well aware of the fact that Drupal Cache API is a remarkable feature introduced in Drupal 8. Still, this topic remains unrevealed to many developers as they consider caching to be a critical aspect of a website. In one of our earlier posts, we have exemplified Cache tags https://www.innoraft.com/blogs/how-does-entity-cache-work-drupal-8. Here is a guide that helps you easily grab in some basic concepts of Cache Context. 

Cache Context is basically a service that helps in creating multiple cached versions of something depending upon the context/request; be it a view, block or any other section on the page. 

For instance, let us consider a block displaying a list of tutorial links on a D8 instance. Now authorised users will be given access to all the links while anonymous ones will be provided only with the free tutorials. This data completely depends upon the role of the user. Hence ‘user.roles’ can be used as a cache context in such a scenario. For simplicity let us assume that there exist only two roles authenticated and anonymous. When an authenticated user hits the page, the version of the block with access to all links will be displayed. Hereafter if another authenticated user visits the page; the cached version of the block will be served thereby enhancing the site performance. When an anonymous user comes to the same page the entire request is carried out and the display with limited access to links will be shown. In such a way we can explicitly decide as to when the cache of the element will be invalidated based on the context.

D8 core provides few predefined cache contexts that are available at https://www.drupal.org/docs/8/api/cache-api/cache-contexts .

Our main focus here would be how to define and use a custom cache_context according to our requirement.

Let us consider a simple example to invalidate the cache of a block that displays a personalised message based on the summary that the user has provided in the user edit page.

Prerequisite: Add a field for filling in summary in the user edit form([base_url]/user/[user_id]/edit) provided by default in drupal.

Cache context can be registered as any other service in the module.services.yml file:

services:
  cache_context.user_summary:
    class: Drupal\example_cache_context\CacheContext\UserSummaryCacheContext
    arguments: ['@current_user']
    tags:
      - { name: cache_context }

   example_cache_context.services.yml

The trick here is to understand the naming convention for a new cache context. The name of the service should be of the format cache_context.* i.e should start with ‘cache_context.’ followed by the appropriate name. Hence the name cache_context.user_summary. Similarly, we can define a further level of hierarchy as well. As per the above snippet, the code for cache context goes into src/CacheContext/UserSummaryCacheContext.php. The service takes in the current_user service as an argument. The detail on how to pass a service as an argument to another is available at https://www.drupal.org/docs/8/api/services-and-dependency-injection/structure-of-a-service-file. We need to tag this service to cache_context as well.

The summary field should be used to create this cache_context logic as per the following code snippet:


<?php

namespace Drupal\example_cache_context\CacheContext;

use Drupal\Core\Cache\Context\CacheContextInterface;
use Drupal\Core\Session\AccountProxy;
use Drupal\user\Entity\User;
use Drupal\Core\Cache\CacheableMetadata;

class UserSummaryCacheContext implements CacheContextInterface {
  /**
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
	protected $user_current;

  /**
  * {@inheritdoc}
  */
	public function __construct(AccountProxy $user_current) {
		$this->user_current = $user_current;
	}

  /**
  * {@inheritdoc}
  */
	public static function getLabel() {
		return t('User Summary cache context');
	}

  /**
  * {@inheritdoc}
  */
	public function getContext() {
    $id = $this->user_current->id();
    $user_details = User::load($id);
    $summary = $user_details->get('field_summary')->getValue()[0]['value'];
    return $summary;
	}

  /**
  * {@inheritdoc}
  */
  public function getCacheableMetadata() {
    return new CacheableMetadata();
  }
}


UserSummaryCacheContext.php

Here the SummaryCacheContext class implements the interface CacheContextInterface. The variable $user_current which is an instance of AccountProxyInterface is declared protected and used as per the arguments mentioned in the services.yml file. The function getContext() contains necessary code for the cache invalidation based on the context. We can implement any other logic according to our requirement here.

The CacheContext code is now ready to use. In order to test the code, create a block within src/Plugin/Blocks and place it on any page:


<?php

namespace Drupal\example_cache_context\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\user\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a block for particular user's summary
 *
 * @Block(
 * id = "user_summary_block",
 * admin_label = @Translation("User Summary Block")
 * )
 */


class UserSummary extends BlockBase {

  /**
   * {@inheritdoc}
   */
	public function build() {
		$build = [];
		$UserId = \Drupal::currentUser()- >id();
		$user = User::load($UserId);
		$summary = $user->get('field_summary')->getValue()[0]['value'];
		$build['user_summary'] = [
			'#markup' => $summary
		]; 
		return $build;
	}

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
  	return Cache::mergeContexts(
  		parent::getCacheContexts(),
  		['user_summary']
  	);
  }
}

 UserSummary.php

To verify if the context has been added properly, use the chrome dev tools:

dev_tools

 

If these headers are not visible you might need to configure settings to enable these as per https://www.drupal.org/docs/8/api/responses/cacheableresponseinterface#debugging

This eliminates the need for cache clearance of entire site after the update of any data which slows down the website. Hope this blog gave a better insight as to the usage of cache context in Drupal 8. You can now invalidate cache as per the context keeping in mind the performance of the site while updating the user of every new piece of information appearing on the website. 

Latest Blogs

How to Choose Web Development Agencies

Points to check before choosing the right website development company

In today’s day and age, where the digital population is increasing exponentially, no credible business can or should refrain from having a strong and steady web presence.

Read More

Wordpress to Drupal

Should I plan to move my Wordpress Website into the Latest Drupal version?

If you're here, I assume you already have a website built on WordPress but you're not satisfied with it, or perhaps you're here to have a clear idea about why you should give a thought to migrating

Read More

Drupal 9 Easy Upgrade

Why is Drupal 9 going to be an easy upgrade compared to the previous major versions?

Lately, there has been a lot of buzz around the corner about Drupal 9. The Drupal community is launching Drupal 9, and as well as have plans for the EOL of Drupal 7 and Drupal 8 by 2021.

Read More

Main-Image-SDLC

Understanding The Software Development Life Cycle (SDLC)

The first question to be asked, should be, “What is The Software Development Life Cycle?”

Read More