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 simple
s 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

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Votre adresse servira à afficher un Gravatar et à vous notifier des réponses. Votre commentaire sera anonymisé si ce billet est dépublié pendant plus de 3 mois.
Pour lutter contre le spam notre système enregistre votre adresse IP et votre adresse e-mail si vous la partagez.
Nous vous invitons à consulter notre politique de confidentialité pour comprendre les traitements faits de ces données et comment les rectifier.

À 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.