Lors des précédents articles de la série sur la Création d'un module avec Drupal 8, nous avons travaillé sur l'entité Internal Link. Nous avons implémenté le type d'entité, nous y avons ajouté les champs et les fonctions nécessaires, nous avons fait en sorte d'avoir un listing digne de ce nom, nous avons également créé un service orienté données pour récupérer des listes d'entités.
Pour cet article, nous allons enchainer avec la configuration globale de notre type d'entité.
Problématique
Avoir un type d'entité, c'est bien. Mettre en place le nécessaire pour qu'il soit fonctionnel, c'est bien aussi. Néanmoins, à l'heure actuelle, la seule chose que nous pouvons faire, c'est créer, éditer et supprimer des entités, mais elles n'existent que pour elles-même.
Hors, le but final est de pouvoir les utiliser pour qu'elles se substituent à des mots ou phrases présents dans des zones de texte.
Comme je l'avais expliqué dans l'article concernant les besoins du module, j'avais une idée bien précise de leur utilisation. Je ne voulais pas implémenter un formatteur de données qui m'aurait obligé à utiliser ce dernier et faire l'impasse sur d'autres formatteurs, ou sans cesse le refactoriser dès lors que les besoins auraient évolués. Je ne voulais pas non plus passer par un filtre d'entrée, qui n'est pas assez souple à mon avis, obligeant à sélectionner le bon format d'entrée de texte et s'appliquant partout ou ce format aurait été sélectionné.
Non, rien de tout ça. Mon but était de pouvoir appliquer les liens internes sur des zones de texte en donnant la possibilité de choisir précisément les champs de type texte sur lesquels le parseur officierait. Je voulais également pouvoir choisir sur quelles entités/bundles les liens internes seraient actifs. Enfin, je voulais avoir deux modes de fonctionnement: un mode manuel qui permettrait au rédacteur de choisir parmi des liens internes lesquels seraient appliqués, et un mode automatique qui appliquerait les liens internes sans intervention. Dans le cas ou les deux modes seraient actifs pour une entité, le mode automatique prendrait le relai dès lors qu'aucun lien interne ne serait sélectionné par l'utilisateur lors de l'édition du contenu.
Réflexion
Lorsque j'ai implémenté ce module avec Drupal 7, la solution s'avérait évidente. J'avais utilisé une déclinaison de hook_form_alter pour altérer le formulaire d'édition des bundles de node (hook_form_node_type_form_alter), ce qui me permettait de rajouter de la configuration pour chaque type de contenu. J'avais ensuite utilisé hook_field_extra_fields pour rajouter un pseudo-champ à chaque type de contenu dont le mode manuel avait été activé, afin de proposer un champ de type liste dans l'entité elle-même, qui permettrait au rédacteur de choisir les liens internes à appliquer.
Avec Drupal 8, j'avoue que je ne savais pas par ou commencer. J'ai d'abord fait une tentative du même type qu'avec Drupal 7, pour me rendre assez vite compte que ça limitait les possibilités. Autant avec D7 un des rares type d'entité viable était node, et il était assez peu probable que les webmasters créent leur propres entités, autant avec D8, la donne est changée. Tout est entité. Limiter uniquement aux noeuds serait une erreur de ma part. Rajouter la configuration uniquement sur les types de contenu noeuds n'était clairement pas une bonne idée. De même, hook_field_extra_fields continue d'exister, mais ne correspond plus du tout à la manière de faire avec Drupal 8. J'étais encore une fois dans l'impasse.
Solutionnement
Après beaucoup de tests, je me suis rendu compte que le portage du module s'avérait bien plus compliqué qu'il n'y paraissait. C'est en observant le fonctionnement d'autres modules, en particulier "Content translation", que j'ai fini par orienter la solution de la configuration globale. Pour le remplacement de hook_field_extra_fields, différentes lectures m'ont convaincu que la solution était d'utiliser l'API Field pour mettre en place un champ spécifique, qui serait attaché aux bundles dès lors que la configuration globale changeait.
Logique d'implémentation
Nous le verrons en détail par la suite, mais la logique d'implémentation va être la suivante:
- Une entité de type ConfigEntityBase pour stocker les différents paramètres de configuration propre à chaque bundle;
- Un formulaire de paramétrage permettant de sélectionner les types d'entité/bundles parmi lesquels nous allons activer les liens internes, et pour chaque bundle, le paramétrage requis;
- Un élément de formulaire (FormElement) qui facilitera l'affichage des options de paramétrage;
- Un champ de type "EntityReference" ainsi qu'un widget de type "Options" (liste de sélection) qui viendra se greffer aux bundles ou les liens internes en mode manuel seront actifs;
Conclusion
Finalement, le passage de Drupal 7 à Drupal 8 aura totalement modifié la manière d'implémenter la configuration globale. Autant prendre les bonnes habitudes dès le départ, même si là ou Drupal 7 demandait l'implémentation de quelques hooks, Drupal 8 oblige un fonctionnement beaucoup plus complexe, comme nous le verrons dans les prochains articles.