Recently, while working on a project I had to build a block to show the user information of the currently logged-in user. Although it's a straight forward thing to do, the important thing to consider here is caching. Setting the cache max-age to 0 have performance impacts if the block has to be shown on all pages. So, the block has to leverage Drupal's Cache API to have a performant block with proper caching.

Caching the block

Since, the block needs to show the current logged in user information so the cache tags had to be set for user ID of the logged-in user with a context of user. But, for an anonymous user since there will be user ID so, the block will just return the parent cache context i.e ContextAwarePluginBase which is extended by BlockBase.


<?php

namespace Drupal\drupal_block_demo\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Link;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\UserStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides an example block.
 *
 * @Block(
 *   id = "drupal_block_demo_example",
 *   admin_label = @Translation("User info"),
 *   category = @Translation("Drupal block demo")
 * )
 */
class ExampleBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * User storage.
   *
   * @var \Drupal\user\UserStorageInterface
   */
  protected $userStorage;

  /**
   * Create a new User block.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The current user object.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, AccountProxyInterface $current_user, UserStorageInterface $user_storage) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->currentUser = $current_user;
    $this->userStorage = $user_storage;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('current_user'),
      $container->get('entity_type.manager')->getStorage('user')
    );
  }

  /**
   * Get the user info.
   */
  protected function getUser() {
    if ($this->currentUser->id() === 0) {
      return FALSE;
    }

    $user = $this->userStorage->load($this->currentUser->id());
    return $user;
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $build['content'] = [
      '#markup' => $this->getUser() ? $this->getUser()->label() : Link::createFromRoute($this->t('Log in'), 'user.login')->toString(),
    ];

    return $build;
  }

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

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    if ($user = $this->getUser()) {
      return Cache::mergeTags(parent::getCacheTags(), ['user', $user->id()]);
    }

    return parent::getCacheTags();
  }

}