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

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

À la découverte des plugins sur Drupal

28 janvier 2022


Que sont les plugins sur Drupal ? Pourquoi sont-ils utiles ? Comment les utiliser ?

Drupal est une référence sur le marché des CMS. Pourquoi ? Parce qu'il est open source certes mais surtout parce qu'il est particulièrement personnalisable et modulable.

Comment fonctionne Drupal ? Un peu comme un lego : on rajoute des briques pour construire l'objet final le plus stable possible.

Le coeur de Drupal est un petit noyau léger. À ce noyau il faut ajouter des modules, des briques logicielles,  qui répondent à un besoin (fonctionnalités) pour construire une application et l'enrichir.

Les plugins quant à eux permettent à un module de fournir des fonctionnalités de manière extensible et orientée objet. Le module définit le cadre de base pour la fonctionnalité et les plugins, regroupés en types, permettent de définir des comportements particuliers.

En quoi consiste le système de plugins sur Drupal ? Découvrons-le ensemble.les plugins sur DrupalDes plugins sur Drupal, pourquoi ?

Sur un CMS comme Drupal, il n'est pas possible (ou en tout cas pas recommandé) de modifier le comportement du site directement en modifiant les sources. Le coeur du CMS est récupéré via un système de gestion de dépendances et les modifications sont donc écrasées dès la première mise à jour venue.

Lorsqu'on veut étendre ou altérer un fonctionnement existant, il faut utiliser les outils fournis par le CMS pour s'attacher au fonctionnement actuel, et voir ce qu'il est possible de faire pour ajouter son propre code.

Sur Drupal, historiquement, ce genre de modifications se faisaient via des "hook". Les hooks sont des fonctions placées dans le fichier principal d'un module (mon_module.module) selon une convention de nommage (function mon_module_menu() pour ajouter des URL au CMS, par exemple).

Avec Drupal 8, si les hooks n'ont pas disparu, d'autres approches sont arrivées avec l'ajout des outils de Symfony. Ainsi, certains comportements, gérés auparavant avec des hooks, sont désormais extensibles avec un EventSubscriber (par exemple, modifier le formulaire responsable de la connexion via une classe étendant RouteSubscriber).

Un autre concept utilisé, et plus largement sur Drupal 8, est le concept de Plugins. Ce concept n'est pas lié à Symfony. C'est un concept propre à Drupal 8+. Il existe nativement différents types de plugins (tels que les blocs), pour lesquels sont définies des manières de lister les plugins existants, les exigences les concernant, et leur utilisation. Pour un type de plugin, il est possible, depuis n'importe quel module, d'ajouter des plugins en respectant ces normes.

 

Exemples de plugins sur Drupal

Les blocs

Voici un exemple de bloc présent dans l'un de nos projets :


<?php
namespace Drupal\gigya_raas\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a 'Gigya RaaS Login' Block
*
* @Block(
*   id = "gigya_rass_login",
*   admin_label = @Translation("Gigya RaaS Login"),
*   category = @Translation("Gigya")
* )
*/
class GigyaRaasLogin extends BlockBase {
/**   * {@inheritdoc}   */
  public function build() {
    $build['block'] = [
      '#theme' => 'gigya_raas_login_block',
      '#showDiv' => \Drupal::currentUser()->isAnonymous(),
    ];
    return $build;
  }
}

L'ajout de ce code permet d'avoir un nouveau bloc dans la page de gestion de blocs, que je peux ajouter dans la région de mon thème actuel, à l'endroit où j'en ai besoin.

Pour le créer, j'ai dû :

  • Créer un fichier dans le dossier [module]/src/Plugin/Block ;
  • Créer une classe qui étend BlockBase. J'aurais pu lui faire implémenter BlockPluginInterface (qu'implémente déjà BlockBase) si je n'avais pas voulu utiliser le code existant dans la classe abstraite fournie par Drupal ;
  • Ajouter les annotations @Block contenant au minimum les attributs "id" et "admin_label" ;
  • Définir la méthode build(), qui retourne de la méthode un render array. Elle aurait pu aussi retourner un string contenant l'HTML du bloc directement.

Une méthode plus rapide (et moins source d'erreur) pour générer son bloc :

vendor/bin/drush generate block

D'autres exemples

Drupal, que ce soit au niveau des fonctions natives (du core) ou de ses modules contribués, propose de nombreux autres types de plugin :

  • Drupal Commerce Custom Pane (un article à venir sur ce sujet) ;
  • FieldFormatter pour gérer le rendu des champs à l'affichage ;
  • ImageEffect pour ajouter des effets supplémentaires aux images (effets visuels, survol ...) ; 
  • et bien d'autres...

Aller plus loin avec les Plugins

Découvrir tous des plugins sur le site

Au delà des types de plugins existants nativement sur Drupal, les modules que vous avez installé sur votre site peuvent avoir déclaré de nouveaux types de plugins, pour lesquels vous ne trouverez pas de liste exhaustive.

Le moyen le plus simple, pour savoir quels sont tous les types de plugins à votre disposition sur votre installation, est d'utiliser la console Drupal (différente de drush) pour lister les types de plugins. Cette console, bien que laissée sans mise à jour, donne toujours accès à des commandes de listing pratiques lors du développement pour lesquelles drush n'a pas d'équivalent à l'heure actuelle.

vendor/bin/drupal debug:plugin

Voir les différents occurrences d'un plugin

Prenons un type de plugin : le queue_worker. Drupal CLI nous apprend qu'il est géré par QueueWorkerManager.

Dans cette classe, nous retrouvons ceci :


public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
  parent::__construct(
    'Plugin/QueueWorker',
    $namespaces,
    $module_handler,
    'Drupal\Core\Queue\QueueWorkerInterface',
    'Drupal\Core\Annotation\QueueWorker'
  );
  $this->setCacheBackend($cache_backend, 'queue_plugins');
  $this->alterInfo('queue_info');
}

Le dernier argument du constructeur permet de connaître la classe d'annotation du plugin (ici Drupal\Core\Annotation\QueueWorker). Je peux donc lister les plugins existants en effectuant une recherche sur "@QueueWorker" (parfois, "@QueueWorker(" limitera plus les résultats).

 

Créer son propre QueueWorker

Pour créer son Worker, il faut à nouveau regarder le Manager et sa classe d'annotation.

  • Le premier argument du construct de DefaultPluginManager est le sous-dossier dans lequel doit se trouver le plugin, ici "Plugin/QueueWorker". Le système de découverte d'un Plugin est hérité de DefaultPluginManager. Il s'agit donc d'une découverte par annotation. C'est la plus utilisée (en savoir plus sur les Discoveries). Pour chaque module déclaré, lors de la génération du cache backend (donc à chaque drush cache:rebuild), Drupal cherche dans tous les modules les fichiers situés dans "src/Plugin/QueueWorker".
  • Le fichier doit contenir une classe qui implémente "Drupal\Core\Queue\QueueWorkerInterface" et donc définir une méthode public function processItem($data).
  • Avec la classe d'annotation, je sais que cette classe doit être annotée d'une définition @QueueWorker qui doit contenir :
    • un id ;
    • un titre (lisible pour les humains) ;
    • un paramètre cron (optionnel) qui va définir le temps par défaut passé par le QueueWorker avant de s'arrêter.

Une fois cela fait, le plugin est trouvé et utilisable par Drupal (lorsqu'une mise en queue se fera sur l'ID du queue worker, il traitera les éléments).

 

Les derivatives

Dans Drupal, on peut créer des blocs de texte et les ajouter dans l'administration des blocs. Comme on l'a vu, il faut créer une classe pour ajouter un bloc. Comment fait Drupal pour créer un bloc différent pour chaque entrée ajoutée en administration ?

Il utilise une Derivative. Une Derivative est une classe d'instruction qui permet de dire à Drupal : "Ce plugin-ci est doit varier selon un processus que je te donne". Dans le cas des blocs, il s'agit de faire varier le plugin BlockContentBlock selon les entrées présentes en base de données :


/**
* Defines a generic custom block type.
*
* @Block( 
*   id = "block_content",
*   admin_label = @Translation("Custom block"),
*   category = @Translation("Custom"),
*   deriver = "Drupal\block_content\Plugin\Derivative\BlockContent"
* )
*/
class BlockContentBlock extends BlockBase implements ContainerFactoryPluginInterface {
  public function getDerivativeDefinitions($base_plugin_definition) {
    $block_contents = $this->blockContentStorage->loadByProperties(['reusable' => TRUE]);
    // Reset the discovered definitions.
    $this->derivatives = [];
    /** @var $block_content \Drupal\block_content\Entity\BlockContent */
    foreach ($block_contents as $block_content) {
      $this->derivatives[$block_content->uuid()] = $base_plugin_definition;
      $this->derivatives[$block_content->uuid()]['admin_label'] = $block_content->label();
      $this->derivatives[$block_content->uuid()]['config_dependencies']['content'] = [
        $block_content->getConfigDependencyName(),
      ];
    }
    return parent::getDerivativeDefinitions($base_plugin_definition);
  }
}

Gérer ses propres types de plugins

Intérêt d'un nouveau type de plugins

Il est tout à fait possible de créer son propre type de plugins dans un module custom. Cette manière de faire a 2 avantages par rapport à un développement "standard" :

  • Un autre module peut ajouter des plugins à utiliser. Lorsque je fais un module amené à se retrouver sur plusieurs sites et qu'il sera une dépendance de ces projets, cela permet d'ajuster son comportement sans avoir différentes versions à gérer. Note : on peut atteindre le même résultat avec des services taggés, à la manière des BreadcrumbBuilder.
  • L'autre point majeur, est la compréhension du code et de la manière de l'étendre par un autre développeur Drupal. En développant sur Drupal, on est amené à utiliser les plugins dans ses projets régulièrement (ne serait-ce que via les blocs) et on sait, en voyant un exemple de plugin, en reproduire un dans son propre module pour étendre les fonctionnalités.

 

Créer son système de plugins

Rappelez-vous du plugin QueueWorker. Une gestion d'un type de plugin a besoin de 2 choses :

  • Une classe Manager, qui s'occupera d'initialiser le Discovery et de spécifier l'interface utilisée ;
  • Une interface de plugin à implémenter.

D'une manière générale, on utilise le système de base de Drupal et on y ajoute :

  • Une classe d'annotations (car on utilisera le système par défaut d'annotations) ;
  • Une classe abstraite de base qui implémente l'interface et permet d'avoir des comportements génériques pour tous les plugins.

A savoir : une commande de génération permet de générer toute la structure de base et d'éviter les erreurs : 

vendor/bin/drush generate plugin-manager

Voici un schéma qui rappelle le fonctionnement d'un type de plugin :

Schéma - fonctionnement des types de plugins Drupal

Savoir créer des plugins de type existant ou créer de nouveaux plugins permet aux développeurs d'implémenter facilement un design pattern "strategy".

 

Les plugins sont utilisés dans de nombreux modules fournis par la communauté de Drupal. Savoir les utiliser, c'est avoir la possibilité d'adapter les modules dits "contrib" aux besoins de votre site.

Vous pourriez par exemple implémenter votre propre passerelle de paiement sur Drupal Commerce ou créer un nouveau mode d'export de résultats pour WebForm.

Image mise en avant pour l'article
Jean-Pascal Langinier
Développeur @ADIMEO
Pourquoi s'abonner à
notre newsletter ?
Pour recevoir tous les mois, un condensé de contenus utiles et pertinents pour votre transformation digitale !