Fil d'Ariane
Drupal 10 : Les types d’entités
Entité de configuration, entité de contenu, classe typée par bundle d'entité, explorons le fonctionnement de ces entités.
Cet article a été initialement rédigé pour Drupal 8 mais son contenu est toujours d'actualité pour Drupal 9 et Drupal 10.
N'hésitez pas à nous contacter ou à vous inscrire à notre formation «Drupal pour les développeurs et développeuses» pour en savoir plus !
Contexte
L’uniformisation débutée avec Drupal 7 pour stocker des données s'est poursuivie et a pris beaucoup de maturité puisque tous les contenus stockés sont maintenant des entités. Celles-ci sont scindées en deux groupes.
Nous avons d’un côté les entités de configuration qui permettent, comme leur nom l’indique, d’exporter de la configuration avec par exemple les paramètres d’affichages des nœuds, les paramètres des blocs, les vues, etc.
De l’autre côté nous avons les entités de contenu qui permettent de stocker les données générées par les utilisateurs. Il s’agit des nœuds, de la taxonomie, des comptes utilisateur, etc.
Entité de configuration
Les entités de configuration n’ont pas de champs. Elles sont des exports de fichiers YAML avec une structure définie dans un fichier <module>/config/schema/<type>.schema.yml.
Les entités de configuration sont exportées via l’API de configuration. Vous aurez besoin de créer un type d’entité de configuration lorsque vous inventerez quelque chose qui peut avoir des sous-types, les données plus simples pouvant être gérées par l’API de configuration directement.
Voici quelques exemples de types d’entités de configuration : des vues, des vocabulaires, des styles d’image, etc.
Des données “simples”, directement gérées par l’API de configuration et exportables peuvent être : le nom du site, l’activation de l'agrégation des JS, la version d’une librairie, etc. (Voir le chapitre dédié à la configuration pour plus de détails).
Entité de contenu
En devenant plus matures, les entités de contenu ont appris à gérer par défaut les révisions, le multilinguisme et l’ajout de champs.
Dans Drupal 7, il était de la responsabilité du contrôleur d’entité de gérer le chargement, la sauvegarde, la suppression et l’affichage des entités. Depuis Drupal 8, ces responsabilités ont été séparées dans plusieurs services typés. Le chargement, la sauvegarde et la suppression sont gérés par le handler de stockage du gestionnaire d'entité (entity manager). Des handlers supplémentaires existent pour gérer le contrôle d’accès, l’affichage, le listing et les formulaires (création, édition).
Type d’entités
Les types d’entités de contenu héritent avec D8 de classes dédiées. Les classes qui définissent les types d’objets sont situées dans src/Entity et implémentent une Annotation, concept qui remplace en partie les hook_info()
de Drupal 7 et qui sera largement abordé dans un chapitre ultérieur.
Exemple :
# User.php
/**
* Defines the user entity class.
*
* The base table name here is plural, despite Drupal table naming standards,
* because "user" is a reserved word in many databases.
*
* @ContentEntityType(
* id = "user",
* label = @Translation("User"),
* handlers = {
* "storage" = "Drupal\user\UserStorage",
* "storage_schema" = "Drupal\user\UserStorageSchema",
* "access" = "Drupal\user\UserAccessControlHandler",
* "list_builder" = "Drupal\user\UserListBuilder",
* "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
* "views_data" = "Drupal\user\UserViewsData",
* "route_provider" = {
* "html" = "Drupal\user\Entity\UserRouteProvider",
* },
* "form" = {
* "default" = "Drupal\user\ProfileForm",
* "cancel" = "Drupal\user\Form\UserCancelForm",
* "register" = "Drupal\user\RegisterForm"
* },
* "translation" = "Drupal\user\ProfileTranslationHandler"
* },
* admin_permission = "administer user",
* base_table = "users",
* data_table = "users_field_data",
* label_callback = "user_format_name",
* translatable = TRUE,
* entity_keys = {
* "id" = "uid",
* "langcode" = "langcode",
* "uuid" = "uuid"
* },
* links = {
* "canonical" = "/user/{user}",
* "edit-form" = "/user/{user}/edit",
* "cancel-form" = "/user/{user}/cancel",
* "collection" = "/admin/people",
* },
* field_ui_base_route = "entity.user.admin_form",
* )
*/
class User extends ContentEntityBase implements UserInterface {
}
A l’instar de Drupal 7, lors de la conception de votre application, si vous imaginez une table dédiée pour stocker vos données, vous aurez probablement besoin de créer un nouveau type d’entité de contenu.
Drupal 9.3 a même introduit des classes par bundle d'entité ! Grâce à cela, il est bien plus simple d'ajouter des méthodes spécifiques à chaque sous-type. Idéal pour traiter de la logique métier proprement.
Champs
Vous rêviez de pouvoir utiliser un formateur sur le titre de vos contenus ?
Drupal l’a fait (enfin presque). Tout est devenu champ dans Drupal 8, les anciennes propriétés sont devenues des champs de base (Base fields) et les champs “traditionnels” s’appellent maintenant des champs configurables (Configurable fields). Tous les champs bénéficient de l’usage des formateurs et des widgets. Les champs de base seront visibles par défaut dans l’interface, les champs configurables le seront après l’écriture d’une ligne de code qui changera le paramètre qui les cache par défaut.
Les champs sont tous typés via la Typed Data API pour palier au faible typage de PHP (exemples : booléen, entier, entity reference, date, changed, uri, uuid, etc). Cela permet lorsque vous manipulez vos données avec l’Entity API de ne pas avoir d'ambiguïté.
Les champs de base sont exposés via la méthode baseFieldDefinitions()
de la classe qui définit le type d’entité. Vous pouvez y indiquer également le libellé, la description, le fait que la propriété soit en lecture seule ou non, exposée ou non dans l’écran de configuration de l’affichage ou des formulaires, etc.
Internationalisation
Tous les contenus étant traduisibles par défaut, plus besoin de manipuler explicitement la langue au niveau des champs (les “und”, “fr” et autre), on indique la langue au niveau de l’entité lors de sa récupération.
// Récupération et utilisation d'une traduction pour la langue active.
$translation = $entity->getTranslation($active_langcode);
$value = $translation->field_foo->value;
// Récupérer le contenu traduit d'une entité.
$entity->getTranslation('it');
// Récupérer un original.
$translation->getUntranslated();
// Récupérer la langue d'une entité.
$translation->language()->id;
Votre commentaire
À propos de Julien
Co-fondateur - Scrum master & Expert technique
Utilisateur de Drupal depuis 2008, j’ai fait mes armes comme développeur chez Commerce Guys puis me suis mis à encadrer les nouveaux arrivants avant de donner des formations, participer aux avant ventes et accompagner les équipes au passage à Scrum.
Je suis impliqué dans la communauté française de Drupal depuis 2009, j’ai été tour à tour président puis vice-président de l’association Drupal France et francophonie entre 2011 et 2013.