Drupal 8 - Comment utiliser une classe spécifique par bundle d'entité ?

30/08/2018

Avec Drupal 8 on travaille avec des entités qui, pour certaines, utilisent des bundles. Sans aller loin dans le détail, les bundles permettent de définir des groupes d'une même entité mais avec des éléments qui diffèrent comme les champs, les view_modes etc... Par exemple, on utilise principalement les bundles pour permettre de définir différents types de node (ex: billet de blog, page éditoriale, etc...).

Les développeurs sont donc amenés à faire des traitements logiques différents en fonction des bundles d'une entité. Ceci est tout à fait faisable en se branchant aux différentes entrées logiques que Drupal 8 propose (soit par hook, soit par événement). Le problème est que ces points d'entrée sont liés aux types d'entité et non aux bundles. Il est donc nécessaire d'effectuer un filtre à chaque point d'entrée pour rediriger vers le bon process en fonction de nos bundles. 

Je vous présente ici le module Bundle Override (https://www.drupal.org/project/bundle_override) qui vous permettra de créer des plugins qui seront les classes qui gèreront vos bundles. Dans un premier temps je vais vous présenter les plugins qui permettent de définir des classes par bundle de nodes et de termes de taxonomie, mais il faut savoir que le module vous permet de créer vos propres managers pour utiliser cette méthode sur n'importe quel type d'entité.

 

Le principe

Pour expliquer le principe je vais prendre l'exemple de l'entité "node". Ce que l'on veut c'est pouvoir définir une classe pour notre bundle "article". Cette classe qu'on appellera "Article" doit :

  1. surcharger la classe Node pour  permettre les interventions lors de l'appel à presave.
  2. permettre la récupération de tous les nodes de type 'article' via la méthode Article::loadMultiple().
  3. permettre la récupération des nodes de type 'article' avec cette même classe lorsqu'on fait appel à Node::loadMultiple().

Pour ces différents besoin, théoriquement il suffit de surcharger la classe NodeStorage, pour que celle-ci nous renvoie telle ou telle classe héritant de Node en fonction du bundle. En pratique, on redéfinie la classe de storage liée à l'entité via le hook 'hook_entity_type_alter'. On utilise ce hook pour signifier à drupal qu'on ne veut plus utiliser NodeStorage mais une classe propre à notre besoin 'NodeBStorage'. 

On va donc utiliser la class 'NodeBStorage' pour ajouter un filtre sur le type de bundle lors de la récupération des nodes via 'loadMultiple' (besoin 2), et également pour nous renvoyer un objet soit de la classe spécifique à notre bundle, soit de la classe Node si le node demandé appartient à un bundle dont la classe n'est spécifiée (besoin 3).

Pour simplifier l'implémentation de notre classe Article, on va la faire hériter d'une classe 'NodeB', qui hérite elle même de la classe Node. Cette classe va nous permettre de gérer plus facilement le filtre à envoyer à la classe NodeBStorage, ainsi que redéfinir proprement la récupération du bundle lié à la classe 'Article' via la méthode statique abstraite 'getStaticBundle' (besoin 1).

Le module Bundle Override permet de recréer cette logique de surcharge successive simplement, et de ne se soucier que de notre classe finale 'Article'.

 

Bundle Override

Installation du module

L'installation du module bundle override n'a rien de particulière. Je vous renvoie vers la page du module ici : https://www.drupal.org/project/bundle_override

Définition du plugin BundleOverrideObjects

Une fois le module installé nous pouvons créer un plugin de type BundleOverrideObjects qui nous permet de définir une classe spécifique à notre bundle de node 'Article' : 

<?php

namespace Drupal\mon_module\Plugin\bundle_override\Objects\node;

use Drupal\bundle_override\Plugin\bundle_override\EntityTypes\node\NodeB;

/**
 * Plugin implementation of the 'article' bundle.
 *
 * @BundleOverrideObjects(
 *   id = "article"
 * )
 */
class Article extends NodeB {

  /**
   * {@inheritdoc}
   */
  public static function getStaticBundle() {
    return 'article';
  }
  
  /**
   * {@inheritdoc}
   */
  public function preSave(){
    // Do what you want here.
  }

}

Attention, il est nécessaire que votre classe étende de la classe NodeB pour qu'elle soit prise en compte par le module. De la même manière le type d'entité est présent dans la structure du répertoire : 
'/Plugin/bundle_override/Objects/node/Article.php'

Une fois cette classe définie et le cache rebuildé, lorsque vous récupèrerez un node de type article, soit directement via Article::load(), soit via la méthode générique Node::loadMultiple, vous récupérerez un objet de type Article. Ainsi tous vos traitements spécifiques à la logique de votre bundle peuvent être centralisés.

 

Aller plus loin 

Vous remarquerez que toute la partie de redéfinition de la classe de storage n'est pas à ré-implémenter puisqu'elle est implémentée dans le module. Le module comprend également une redéfinition du storage  pour les termes de taxonomie. 

Il faut noter que si vous désirez utiliser ce système pour d'autres types d'entités, le module vous permet de le faire simplement en gardant la logique expliquée précédemment. Je ferai un autre article dédié à la mise en place de ce système pour des entités quelconques ultérieurement. Je vous propose de vous rapprocher de la documentation en attendant .

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