Block caching examples

In the following examples, we'll be utilizing a custom block to illustrate the workings of caching in both Drupal 9 and 10. However, it's essential to note that these principles and techniques can be equally applied to any rendered array within the Drupal framework.

Disable cache

There are two distinct methods to disable cache for a custom block within Drupal. One effective approach involves the use of the UncacheableDependencyTrait trait. Let's delve deeper into this:

<?php

namespace Drupal\test_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\UncacheableDependencyTrait;

/**
 * Provides a 'Test block' Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test block"),
 *   category = @Translation("Test block"),
 * )
 */
class TestBlock extends BlockBase {

  use UncacheableDependencyTrait;

  public function build() {
    return [
      '#markup' => $this->t('Time is: ') . time(),
    ];
  }

}

Alternatively, you can manipulate the cache max-age setting to control caching. Here's how you can do that:

<?php

namespace Drupal\test_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'Test block' Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test block"),
 *   category = @Translation("Test block"),
 * )
 */
class TestBlock extends BlockBase {

  public function build() {
    return [
      '#markup' => $this->t('Time is: ') . time(),
      '#cache' => [
        'max-age' => 0,
      ],
    ];
  }

}

If you come across a recommendation to use:

\Drupal::service('page_cache_kill_switch')->trigger();

to disable cache for a single block, it's best to avoid this approach. This is because page_cache_kill_switch service is intended to disable cache for an entire page per request, not just individual blocks, and can lead to unnecessary performance degradation.

Cache contexts

If you prefer not to completely disable the cache for a block, the use of cache contexts is an effective alternative. For instance, the user cache context can be implemented. This particular cache context allows the block to be cached for each unique user. This approach is particularly useful when the block content is specific and unique to each user.

<?php

namespace Drupal\test_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'Test block' Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test block"),
 *   category = @Translation("Test block"),
 * )
 */
class TestBlock extends BlockBase {

  public function build() {
    $email = \Drupal::currentUser()->getEmail();

    return [
      '#markup' => $this->t('Your email is: ') . $email,
      '#cache' => [
        'contexts' => [
          'user',
        ],
      ],
    ];
  }

}

If this caching strategy isn't properly implemented, you may encounter issues. For instance, the first user who visits the site will see the correct email address. However, all subsequent users may end up seeing the first user's email address instead due to incorrect cache behavior. This emphasizes the importance of correctly managing caching in Drupal, especially when dealing with user-specific data.

For a comprehensive list of available cache contexts, please refer to the provided link.

Cache tags

Finally, I'd like to introduce you to another vital aspect of caching - cache tags. Imagine a scenario where the content of a block is dependent on a specific node. In this case, you'll want to ensure that each time the node is updated, the cache for that block is invalidated, thereby ensuring that your site displays the most recent information. Here's how you can accomplish this:

<?php

namespace Drupal\test_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'Test block' Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test block"),
 *   category = @Translation("Test block"),
 * )
 */
class TestBlock extends BlockBase {

  public function build() {
    $node = \Drupal\node\Entity\Node::load(1);
    $node_title = $node->getTitle();

    return [
      '#markup' => $this->t('The title of node #1 is: ') . $node_title,
      '#cache' => [
        'tags' => ['node:1'],
      ],
    ];
  }

}

IMPORTANT NOTE: It's crucial to note that all the above-mentioned caching strategies apply exclusively to authenticated (logged-in) users! In our next discussion, we'll explore how caching operates for anonymous users within the Drupal system. Stay tuned for that!

About the Author

Goran Nikolovski is an experienced web and AI developer skilled in Drupal, React, and React Native. He founded this website and enjoys sharing his knowledge.