Drupal 8 - Gestion du cache - Créer un contexte custom

10/02/2019

Dans cet article, je vais vous présenter la méthode pour créer un contexte de cache custom. Si vous ne savez ni ce que c'est ni à quoi ça sert, je vous encourage à lire le premier article de la série sur le cache.

Créer un contexte de cache custom

Avant de commencer

Les contextes de cache sont gérés via des services, et il est tout à fait possible de créer votre propre contexte de cache. Attention il existe déjà un très grand nombre de contextes de cache déjà proposés, donc avant de créer le votre, vérifiez que ce dont vous avez besoin n'existe pas déjà.

Le principe

Imaginons que nous voulions une page qui affiche "Bonne journée" entre 3h et 17h59 et "Bonne soirée" entre 18h et 2h59h. On va devoir créer un contexte de cache qui va nous permettre de générer deux rendus différents. Et de ne les générer qu'une seule fois, c'est tout le principe du cache.

Pour cela, il va falloir déclarer un service qui nous renvoie un identifiant de contexte.

Il y a quelque chose de confusant dans le naming des éléments de Drupal à ce sujet. En réalité, on ne crée pas de service de contexte de cache mais un service qui permet de renvoyer le contexte courant, ce qu'on pourrait appeler un ContextIdentifier (mais qui en fait est appelé un CacheContext). Ce ContextIdentifier fournit l'identifiant du contexte dans lequel on se trouve via la méthode getContext. 

Cet identifiant de contexte sert de clé permettant de récupérer le bon rendu selon le contexte courant. Par exemple, ici, nous n'aurons que deux identifiant de contextes différents :

- Entre 3h et 17h59, identifiant : 0

- Entre 18h et 2h59, identifiant : 1

Nous allons partir du principe que nous avons un module "politesse", dans lequel nous allons déclarer notre contexte de cache, autrement dit notre service ContextIdentifier. Nous allons ensuite ajouter ce ContextIdentifier à un node en passant par un preprocess. Je passe volontairement par un preprocess pour l'exemple, mais on aurait pu utiliser un block pour rendre le message, voire utiliser le module bundle_override pour surcharger la classe node et redéfinir la méthode getCacheContexts (un peu d'autopromotion ne fait jamais de mal)

 

Déclaration du service ContextIdentifier

La première étape va être de déclarer le service ContextIdentifier qui va permettre d'identifier le contexte dans lequel nous nous trouvons.

Attention, j'ai eu quelques problèmes pour que mon service soit bien pris en compte. Il semblerait que ce service soit géré un peu à la manière des plugins, et donc il doit être dans un répertoire particulier de votre module, en l'occurrence Cache/Context, pour être reconnu, et surtout, l'identifiant du service doit être préfixé par cache_context.

La première étape est donc de déclarer le service dans politesse.services.yml :

services:
  cache_context.politesse_cache_context:
    class: \Drupal\politesse\Cache\Context\PolitesseCacheContextIdentifier
    tags:
      - { name: cache.context }

Et ensuite, on crée la classe : 

<?php
namespace Drupal\politesse\Cache\Context;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;

/**
 * Définit un contexte de cache spécifique pour le module politesse.
 * 
 * Permet de générer un contexte de cache différent entre 3h et 17h59 
 * et entre 18h et 2h59h
 *
 * Cache context ID: 'politesse_cache_context'.
 */
class PolitesseCacheContextIdentifier implements CacheContextInterface {
  
  /**
   * Le contexte
   *
   * @const string
   */
  const CONTEXT_ID = 'politesse_cache_context';

  /**
   * Retourne le service
   * 
   * @return static
   *   Le service.
   */
  public static function me(){
    return \Drupal::service('cache_context.politesse_cache_context');
  }

  /**
   * {@inheritdoc}
   */
  public static function getLabel() {
    return t('Cache contexte sur la notion entre 3h et 17h59 et entre 18h et 2h59h');
  }

  /**
   * Renvoie l'identifiant de contexte courant.
   * 
   * On renvoie un identifiant de contexte différent 
   * 1 si on est entre 3h et 17h59
   * 0 si on est entre 18h et 2h59h
   *
   * {@inheritdoc}
   */
  public function getContext() {
    // Si des paramètres d'urls sont passé, on renvoi params.
    return $this->itsMorning() ? 1 : 0;
  }

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

  /**
   * Renvoie vrai si l'appel est fait entre 3h et 17h59.
   * 
   * @return boll
   *   L'état de la journée.
   */
  public function itsMorning(){
     $currentDate = intval(date('Hm'));
     return $currentDate >= 300 && $currentDate < 1800;
  }

}

Pas de difficulté ici, la méthode getContext renvoie l'identifiant de contexte courant comme voulu.

Ajout du preprocess

Il est donc maintenant temps de déclarer le preprocess dans lequel on va ajouter les données. Pour ceci, on crée le fichier politesse.module : 

<?php

use \Drupal\politesse\Cache\Context\PolitesseCacheContextIdentifier;

/**
 * Implements hook_preprocess_node().
 */
function politesse_preprocess_node(&$variables) {
  // On récupère le contexte courant.
  if( PolitesseCacheContextIdentifier::me()->itsMorning() ){
    $variables['message'] = t('Bonne journée');
  }
  else{
    $variables['message'] = t('Bonne soirée');
  }
}

Si vous testez ceci, cela va fonctionner... une seule fois. Vous allez passer une fois dans ce preprocess. La variable message va s'instancier correctement et le rendu va se mettre en cache. Mais il sera mis en cache indéfiniment. Ainsi lorsque vous changerez de contexte (quand vous passerez d'une tranche horaire à l'autre), rien n'indiquera à Drupal d'aller chercher (ou de générer) un nouveau rendu.
Pour cela, il est nécessaire d'ajouter la notion de cache contexte dans le paramètres $variables : 

On ajoute alors l'identifiant du service du PolitesseCacheContextIdentifier dans le tableau de contextes que va checker l'entité à rendre.

<?php

use \Drupal\politesse\Cache\Context\PolitesseCacheContextIdentifier;

/**
 * Implements hook_preprocess_node().
 */
function politesse_preprocess_node(&$variables) {
  // Ajout de la dépendance au contexte.
  $variables['#cache']['contexts'][]  = PolitesseCacheContextIdentifier::CONTEXT_ID;

  // On récupère le contexte courant.
  if( PolitesseCacheContextIdentifier::me()->itsMorning() ){
    $variables['message'] = t('Bonne journée');
  }
  else{
    $variables['message'] = t('Bonne soirée');
  }
}

 

Conclusion

Voilà, grâce à cette méthode vous vous assurez de pouvoir gérer vos propres contextes de cache. Le but étant de générer le maximum de cache possible tout en restreignant au maximum le nombre de rendus produits. Ainsi vous conjuguez performance pour l'utilisateur (en fournissant un rendu rapide grâce au cache) et utilisation raisonnée des ressources (en ne générant des caches que pour des contextes bien définis).

Ajouter un commentaire

HTML restreint

  • Balises HTML autorisées : <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Les adresses de pages web et les adresses courriel se transforment en liens automatiquement.
Votre email ne sera pas publié mais permettra à l'administrateur de vous recontacter en cas de problème