Afficher un champ individuel d'un contenu avec Drupal 8

Une goutte d'eau sur une feuille

On peut avoir besoin parfois d'obtenir le rendu HTML d'un unique champ d'un contenu ou d'une entité. Par exemple pour un affichage simplifié de contenus relatifs au contenu consulté, l'utilisation de champs spécifiques dans d'autres contextes, etc. Obtenir de façon programmatique le rendu d'un champ peut être problématique pour tout le système d'invalidation des caches de Drupal 8, car le render array obtenu ne contiendrait pas les cache tags de l'entité source, et ainsi ce champ affiché de façon individuelle dans un autre contexte ne pourra pas être invalidé si le noeud source venait à être mis à jour.

Utiliser les modes d'affichages des entités

Pour pallier à ce problème, et s'éviter à devoir commencer à gérer les cache tags de façon manuelle (Drupal 8 le fait déjà très bien, et certainement mieux), nous pouvons opter pour un mode d'affichage spécifique pour l'entité en question, et ce mode d'affichage ne contiendra que le champ que nous souhaitons afficher. Ainsi nous obtiendrons le rendu de ce champ de façon individuelle, en affichant simplement ce contenu dans ce mode d'affichage spécifique, et ainsi tous les cache tags ou cache context liés à notre contenu source seront ajoutés automatiquement à la page dans laquelle ce champ sera utilisé. Et nous n'avons pas à gérer les caches tags du noeud source, en lieu et place du coeur de Drupal 8.

Récupérer et afficher un champ individuel de façon programmatique

Cette précédente solution vaut si ce type de besoin reste marginal, et le nombre de champ à afficher individuellement est limité, car si nous devions créer autant de modes d'affichage que de champs à afficher individuellement, pour un très grand nombre de champ, cette option pourrait s'avérer vite indigeste, chronophage et très pénible à maintenir.

Nous pouvons alors recourir à quelques lignes de codes pour récupérer ce champ individuel, et pouvoir l'injecter dans n'importe quelle page.

Ce snippet va nous permettre de récupérer le render array d'un champ de façon individuelle.

/**
 * Implements hook_preprocess_HOOK().
 */
function my_module_preprocess_node(&$variables) {
  /** @var \Drupal\node\NodeInterface $node */
  $node = $variables['elements']['#node'];

  $entity_type = 'node';
  $entity_id = 2;
  $field_name = 'body';

  /** @var \Drupal\node\NodeInterface $source */
  $source = \Drupal::entityTypeManager()->getStorage($entity_type)->load($entity_id);
  $viewBuilder = \Drupal::entityTypeManager()->getViewBuilder($entity_type);
  $output = '';

  if ($source->hasField($field_name) && $source->access('view')) {
    $value = $source->get($field_name);
    $output = $viewBuilder->viewField($value, 'full');
    $output['#cache']['tags'] = $source->getCacheTags();
  }

  if ($node->id() == '1') {
    $variables['content']['other_body'] = $output;
  }

}

Dans cet exemple, nous récupérons le champ body d'un contenu (le noeud avec l'identifiant 2), reconstruisons son render array dans le mode d'affichage Contenu complet (full), puis nous ajoutons au tableau obtenu le cache tag du noeud source (node:2). Et enfin, pour conclure nous ajoutons ce champ dans les variables fournis au template Twig pour le noeud dont l'identifiant est 1.

Sans l'ajout du cache tag du noeud source avec la ligne ci-dessous, alors si le noeud source est mis à jour, son champ body rendu dans un autre contexte restera identique. Ou pire, si nous dépublions ce contenu source, une partie de son contenu sera toujours visible dans un autre contexte  

$output['#cache']['tags'] = $source->getCacheTags();

Il suffit de vérifier dans les entêtes de la page sur le contenu (node 1), sans cet ajout, seul le cache tag du noeud courant est présent (node:1).

Entêtes de la page et debug des cache tags

 

En ajoutant le cache tag du noeud source dans le render array du champ individuel, nous nous assurons que ce champ sera toujours à jour, quel que soit le contexte où il est affiché. Ainsi on peut vérifier les cache tags du contenu (node 1) sur lequel nous avons injecté ce champ provenant du node 2.

Entêtes de la page et debug des cache tags

 

Nous avons bien désormais le cache tag node:2 présent dans les entêtes, nous assurant que cette page sera invalidée dès que le contenu source sera modifié. Nous pouvons donc maintenant, en toute tranquillité, recourir à cette méthode pour injecter des champs de façon individuelle d'une entité dans un autre contexte.

Ne pas oublier les cache tags (entre autres), et durant la phase de développement, penser à activer le cache de façon régulière (qui ne désactive pas les caches de façon définitive durant le développement ?) sont quelques règles de base à garder en tête. Nous avons pu le voir, le cache, sa gestion, son invalidation, doivent faire partie intégrante du processus de développement, sans quoi on risque de faire face très certainement à quelques désillusions lors de la mise en production d'un projet Drupal 8.

 

Ajouter un commentaire