Quickie 03 : Déménager la logique métier d'un preprocess vers une classe d'entité

08 Avril 2024 · 5 Min · 2
Image d'un petit robot cute transportant des cartons.

J'ai remarqué que sur la plupart des projets réalisés, j'utilise énormément les preprocess de rendu d'entité afin ajouter des données pour m'en servir dans les templates.
Depuis quelques temps je passe par la redéfinition de la classe sur une entité précise afin d'avoir un maximum de code métier dans ces dernières.
Voici ma méthode pour transférer des données ou conditions complexes aux templates d'entité.  

Ma méthode depuis Drupal 7


Certaines fois on peut avoir besoin d'ajouter de la data ou des conditions dans les templates d'entité. Jusqu'à récemment je mettais soit directement la condition dans le template si elle était simple, soit je déclarai un preprocess afin d'ajouter une variable contenant le résultat de la condition qui sera ensuite transmise au twig.
Voici un exemple :

/**
 * Implements hook_preprocess_HOOK().
 */
function mymodule_preprocess_node__article(&$variables) {
  $variables['new_article'] = (time() - $variables['node']->getCreatedTime()) < 604800; // Article créé il y a moins de 7 jours
}

Ceci me permettais directement depuis le template node--article.html.twig d'avoir une condition simple en lecture.

{% if new_article %}
   <span class="article__is_new">{{ 'Nouveau'|t }} </span>
{% endif %}

Nouvelle façon de faire

Pour continuer d'enlever un maximum de code procédurale des interminables fichiers .modules, je déporte maintenant ces règles dans des classes d'entité. Pas touche aux fichiers de Drupal alors on utilise le système d'altération qui est possible grâce à ..... un hook.😮‍💨
On échappe pas au hook mais il ne prendra que quelques lignes et tout le code métier, qui peut être composé de plusieurs dizaines ou centaines de lignes, sera mis dans une classe.
Par défaut chaque entité possède sa classe de "gestion". C'est notamment à cet endroit que j'interviens pour fournir une classe alternative qui étendra la classe d'origine. 
L'exemple est fait sur un nœud donc il faut changer la classe du nœud de type article via le hook HOOK_entity_bundle_info_alter()


/**
 * @hook HOOK_entity_bundle_info_alter()
 */
function mymodule_entity_bundle_info_alter(array &$bundles): void {
  if (isset($bundles['node']['article'])) {
    $bundles['node']['article']['class'] = Drupal\mymodule\Entity\Article::class;
  }
}

On définie une nouvelle classe Article uniquement pour les nœuds de type article.
Il faut maintenant créer cette classe qui doit se trouver dans src/Entity.

Fichier modules/custom/mymodule/src/Entity/Article.php :

namespace Drupal\mymodule\Entity;
use Drupal\node\Entity\Node;
class Article extends Node {
  const TIME_TO_CONSIDER_NEW = 60*60*24*7; // 7jours
  
  /**
   * Renvoie true si l'article date d'il y a moins de 7 jours
   *
   * @return bool
   */
  public function isNewArticle(): bool{
    return (time() - $this->getCreatedTime()) < self::TIME_TO_CONSIDER_NEW;
  }
 
}

Comme la classe contient le code métier, il est maintenant possible d'appeler cette méthode à chaque fois qu'un nœud de type Article est chargé. Notamment depuis le fichier twig node--article.html.twig

{% if node.isNewArticle() %}
     <span class="article__is_new">{{ 'Nouveau'|t }} </span>
{% endif %}

 

Petit message d'avertissement !
 

Faites attention aux noms des méthodes que vous allez créer. Il existe une liste avec tous les noms de méthode ou de préfixe autorisés depuis un appel dans un template Twig. Si vous préfixez vos noms de méthodes avec "get", "has" ou "is" il n'y aura pas de problèmes.
Si vous n'êtes pas dans ce cas alors vous devrez ajouter votre préfixe ou nom de méthode à la liste blanche via le code ci-dessous.
 

Dans le fichier settings.php ou settings.local.php

$settings['twig_sandbox_allowed_methods'][] = 'monNomDeMéthode';
$settings['twig_sandbox_allowed_prefixes'][] = 'monPréfixe';


2 commentaires
Tête d'alien
Greg
08 Avril 2024 · 12h35

Salut petite question, à quel moment on va décider d'utiliser nos propres méthodes dans une class ou bien se créer un service pour gérer nos fonctions ?

Tête d'alien
Zafiriou-Lejeune Mickaël
22 Avril 2024 · 08h10

D'un point de vue personnel, tout ce qui est algo appliqué à une entité qui es chargée je le fais via cette réécriture de classe d'entité sauf si ce besoin est crossEntity. Par exemple un méthode qui fait un truc précis mais peut importe que ce soit un noeud, taxo ou autre.
Les services je les utilises quand il s'agit d'un ensemble de méthode répondant à un besoin précis ou pour factoriser du code afin d'éviter les code dupliqués.

Exemple, j'utilise les classes d'entités pour écrire des conditions complexes qui check la valeur de certains champs. Là c'est directement lié au bundle et une fois l'entité chargée donc classe d'entité.

En revanche j'utilise un service pour mes requêtes d'entité. Ça évite de réécrire les entityQuery qui peuvent être utilisés à plusieurs endroits où justement, les entités ne sont pas encore chargées.

Ajouter un commentaire

Texte brut

  • Aucune balise HTML autorisée.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Les adresses de pages web et les adresses courriel se transforment en liens automatiquement.
CAPTCHA
Cette question sert à vérifier si vous êtes un visiteur humain ou non afin d'éviter les soumissions de pourriel (spam) automatisées.