close

Découvrez nos offres pour faire du digital le levier de votre croissance !

download
Modèles

Téléchargez le Guide Ultime de gestion de projet digitale pour vous aider à piloter vos transformations et faire les bons choix !

Image mise en avant pour l'article

Lazy Builder : créer des contenus ultra-dynamiques dans Drupal

22 janvier 2024
Vous êtes à la recherche d'une solution efficace pour créer des contenus dynamiques sans sacrifier les performances de votre site Web ? Le lazy builder est certainement la solution.


Dans Drupal, le lazy builder offre la possibilité de générer un contenu hautement dynamique au sein d'un tableau de rendu, sans nécessiter la désactivation du cache pour l'intégralité de celui-ci ou de la page à laquelle le contenu est lié.

En d'autres termes, le tableau de rendu initial peut bénéficier d'une mise en cache importante, tandis que les lazy builders permettent d'effectuer un rendu additionnel après le premier passage de rendu pour créer du contenu.

Sur la photo, un développeur web travail sur un projet digital

Quand utiliser un lazy builder ?

En règle générale, vous devriez envisager d'utiliser un lazy builder chaque fois que le contenu que vous ajoutez à un tableau de rendu appartient à l'une des catégories suivantes :

  • Contenu qui aurait une cardinalité élevée s'il était mis en cache. Par exemple, un bloc affichant le nom de l'utilisateur. Il peut être mis en cache, mais parce qu'il varie d'un utilisateur à l'autre, il pourrait "saturer" la table du cache.
  • Contenu qui ne peut pas être mis en cache ou qui a un taux d'invalidation très élevé. Par exemple, des statistiques qui doivent toujours être aussi à jour que possible.
  • Contenu qui nécessite un processus d'assemblage long et potentiellement lent.

Une mise en situation pour commencer

Dans cet article, j'ai choisi d'illustrer mes propos par un exemple concret. Ici, l'exemple que j'ai retenu est celui d'un bloc qui affiche les derniers articles consultés par l'utilisateur courant. Nous sommes donc dans le premier et le deuxième cas du précédent paragraphe.

En effet, afficher les derniers contenus consultés nécessiterait d'invalider le cache pour chaque utilisateur connecté, et ceci, à chaque consultation d'un contenu (des articles dans notre exemple).

Nous pourrions utiliser les caches contexts. Mais ce type de cache n'est pas appliqué lorsque des pages mises en cache sont servies à des utilisateurs anonymes et que le module Internal Page Cache (page_cache) est activé.

Une solution à proscrire serait de paramétrer le cache "max-age" à 0 pour notre bloc custom. Ce qui impliquerait l'invalidation du cache pour toute la page où se trouve le bloc.
Au revoir les performances…

Vous avez des questions sur la gestion du cache dans Drupal, je vous propose de lire notre article : "Le cache Drupal : pour une meilleure gestion des performances !"

 

Quelles sont les caractéristiques du lazy builder ?

Le Lazy builder se caractérise par 3 clés :

  • #lazy_builder : dans un tableau de rendu, vous pouvez définir une clé spéciale "#lazy_builder" pour indiquer que la construction de l'élément de rendu doit être retardée jusqu'à ce qu'il soit réellement nécessaire. Cela permet d'économiser des ressources en ne générant que le rendu nécessaire au moment opportun.
  • #create_placeholder : une autre approche consiste à utiliser la clé "#create_placeholder" dans un tableau de rendu pour indiquer à Drupal de créer un espace réservé (placeholder) pour l'élément de rendu. Cet espace réservé est remplacé par le rendu réel au moment de l'affichage.
  • #cache : les Lazy builders de Drupal bénéficient également de la gestion avancée du cache pour optimiser les performances. En utilisant la clé "#cache" dans le tableau de rendu, vous pouvez définir des paramètres spécifiques pour le stockage en cache de l'élément de rendu. Drupal peut ainsi éviter de recalculer l'élément à chaque requête, en utilisant la version mise en cache lorsque cela est possible.

Pour vous aider, je vous donne un exemple de déclaration d'un Lazy builder :


$build['content'] = [
      '#create_placeholder' => TRUE,
      '#lazy_builder' => [
        'your_lazy_builder_service_name_declaration:your_callback_method', [arg1],
      ],
    ];

    return $build;

De plus, il est possible de passer des arguments à la classe Lazy builder à conditions que ceux-ci soient de type primitif (string, bool, int, float, NULL). On ne peut pas, par exemple, passer un objet "User" en argument… mais son id oui ! 😉

Enfin, pour être vraiment complet, il existe plusieurs formats de Callback :

> Format 1 :


use Drupal\render_example\LazyBuilder;
$build['content']['#lazy_builder'] = [LazyBuilder::class . '::build', ['arg1', 'arg2']];

> Format 2 :


$build['content']['#lazy_builder'] = [static::class . '::build', ['arg1', 'arg2']];

> Format 3 :


$build['content']['#lazy_builder'] = ['service.name:method', ['arg1', 'arg2']];

Pour notre exemple, j'ai choisi de créer un Service (format numéro 3). Il faudra donc faire référence au service par son nom.


La création de notre lazy builder

Sans plus attendre, passons à la création de la classe Lazy builder.

Celle-ci doit implémenter la classe TrustedCallbackInterface (Drupal\Core\Security\TrustedCallbackInterface)


namespace Drupal\your_custom_module;

use Drupal\Core\Security\TrustedCallbackInterface;

/**
 * Class LastViewedContentLazyBuilder
 * To render the last viewed content using Lazy Builder.
 *
 * @package Drupal\your_custom_module
 */
class LastViewedContentLazyBuilder implements TrustedCallbackInterface {

  /**
   * {@inheritdoc}
   */
  public static function trustedCallbacks(): array {
    return ['renderLastViewedContent'];
  }

  /**
   * Lazy builder callback to render the last viewed content
   * independently of the page cache.
   *
   * @return array
   *   The render array.
   */
  public function renderLastViewedContent(): array {
    $build = [];
    // Current user ID.
    $uid = \Drupal::currentUser()->id();
    // Your custom service to retrieve the last viewed content.
    $lastViewedContent = \Drupal::service('your_custom_service')
      ->getLastViewedContent($uid);

    $build['content'] = [
      '#theme' => 'last_viewed_content',
      '#content' => $lastViewedContent,
      '#cache' => [
        'contexts' => ['user'],
        'tags' => [
          'node_list:article',
        ],
      ],
    ];

    return $build;
  }

}

L'interface TrustedCallbackInterface nous oblige à implémenter la méthode trustedCallbacks() qui doit retourner un tableau (string[]) qui permet de déclarer le callback. Ici, notre callback déclaré est renderLastViewedContent.

Dans la méthode associée, rien de bien compliqué. Nous faisons appel à notre service custom qui récupère les derniers articles consultés pour l'utilisateur courant et nous renvoyons un tableau de rendu contenant un thème custom "last_viewed_content", une variable "#content" qui contient les articles "buildés" ainsi qu'une gestion du cache qui permet de s'assurer que les contenus correspondent bien à l'utilisateur courant.

Le cache tags node_list:article permet de mettre à jour la liste si jamais un article a été supprimé.

NB : En situation réelle, il est préférable d'utiliser l'injection de dépendances plutôt que d'utiliser les méthodes statiques de la classe Drupal.

Et un dernier point, il ne faut pas oublier de déclarer notre lazy builder dans le fichier your_custom_module.services.yml


services:

  # --- Lazy Builders ---
  your_custom_module.last_viewed_content.lazy_builder:
    class: Drupal\your_custom_module\LastViewedContentLazyBuilder
    arguments:
      - '@your_custom_service.service'
      - '@another_custom_service.service'
    tags:
      - { name: lazy_builder }

La création du bloc Custom

Il ne nous reste plus qu'à créer un bloc custom qui renverra le lazy builder !


namespace Drupal\your_custom_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a "Last viewed content" block using Lazy Builder.
 *
 * @Block(
 *   id = "last_viewed_content_block",
 *   admin_label = @Translation("Last viewed content block Lazy Builder"),
 *   category = @Translation("Custom"),
 * )
 */
final class LastViewedContentBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build(): array {
    $build['content'] = [
      '#create_placeholder' => TRUE,
      '#lazy_builder' => [
        'your_custom_module.last_viewed_content:renderLastViewedContent', [],
      ],
    ];

    return $build;
  }

}

Le contenu du bloc sera affiché indépendamment du reste de la page.

La hiérarchie de votre module devrait ressembler à ceci :

Capture d'écran du résultat de la hiérarchie de votre module lazy builder

Pour tester et simuler le rendu, vous pouvez ajouter un sleep(5); dans la méthode callback du lazy builder. La totalité de la page sera affichée et cinq secondes après le contenu du bloc custom apparaitra.


    sleep(5);

    return $build;


En conclusion, pour optimiser les performances des sites Web, le lazy builder devient un outil puissant, en ne générant que le contenu nécessaire, au moment opportun, tout en exploitant intelligemment la mise en cache.

Voilà, j'espère que cet article vous a plu, je vous conseille de poursuivre votre lecture avec 2 articles de notre blog technique :

Crédit photo : shironosov

Image mise en avant pour l'article
Vivien Barbeau
Développeur Web
Webinar
Drupal 10, qu'est-ce qui change concrètement ?
Voir le webinar !
Besoin d'aide pour migrer votre site vers Drupal 10 ?
Notre équipe d'experts "Drupal" vous accompagne dans votre projet de migration vers Drupal 10 !
Contactez-nous !
Pourquoi s'abonner à
notre newsletter ?
Pour recevoir tous les mois, un condensé de contenus utiles et pertinents pour votre transformation digitale !