Maîtriser les EntityQuery : Guide pratique et exemples concrets

Rédigé par Teddy Vermeulin le 08/04/2024

Dernière mise à jour le 26/04/2024

Découvrez toutes les subtilités des requêtes sur les entités dans Drupal.

À la découverte des EntityQuery :

Jouons avec les entités dans Drupal 10

Sous Drupal 10, l'architecture est conçue autour du concept central que presque tout est une entité, ce qui enrichit considérablement les possibilités de manipulation et de requête des données. 

Cette approche unifiée permet aux développeurs de lancer des requêtes sur une variété d'éléments tels que les noeuds (articles, pages), les blocs, les utilisateurs, les menus et les éléments individuels de ces menus. 

Il est également possible de travailler avec des types d'entités personnalisés, adaptés aux besoins spécifiques de votre projet. 

Une requête simple

Les entityQuery se construisent toujours de la même façon. 

Nous déclarons le type d'entité sur laquelle nous souhaitons effectuer la requête :
$query = \Drupal::entityQuery("entity");

Nous précisions à Drupal, s'il doit effectuer un contrôle d'accès sur les entités qui résulteront de la requête :
$query->accessCheck(TRUE);

Enfin, nous lançons la requete pour récupérer une liste d'identiifants :
$ids = $query->execute();

$query = \Drupal::entityQuery("entity"); $query->accessCheck(TRUE); $ids = $query->execute(); $entities = Entity::loadMultiple($ids);

Les conditions

Pour exploiter pleinement les requêtes, nous pouvons leur ajouter des conditions. 

Elles se présentent sous cette forme :

$query->condition("field_name", "value", "operator");

Les trois variables sont :

  1. field_name ou property :
    Le nom du champ ou la propriété que l'on souhaite évaluer.
     
  2. value :
    La valeur que nous souhaitons évaluer.
     
  3. operator :
    L'opérateur qui va permettre l'évaluation. Par défaut, l'opérateur est =. Mais il peut être :
    1. < : plus petit que la valeur ;
    2. <= : plus petit ou égal à la valeur ;
    3. > : plus grand que la valeur ;
    4. >= : plus grand ou égal à la valeur ;
    5. <> : différent de la valeur ;
    6. IN : la ou les valeurs sont incluses dans le champ que l'on évalue ;
    7. like : contient la valeur.
// Nous voulons récupérer les entités du type "node" $query = \Drupal::entityQuery('node'); // Nous souhaitons qu'il y est un contrôle d'accès $query->accessCheck(TRUE); // Les nodes doivent être du type article $query->condition("type", "article"); // Et le titre dans contenir la chaîne de caractères contenu dans la variable $string // Nous utiliserons la fonction db_like pour réaliser un échappement de la chaîne de caractères $query->condition('title', \Drupal::database()->escapeLike($string) . '%', 'like'); // Ils doivent avoir les étiquettes avec les id 5 et 6 $query->condition("field_tags", [5,6], "IN"); // Après avoir ajouté toutes nos conditions, nous pouvons récupérer les ids $node_ids = $query->execute(); // Et charger les noeuds $nodes = Node::loadMultiple($node_ids);

Les conditions avec opérateurs logiques

Pour effectuer des requêtes plus complexes, nous pouvons utiliser les groupes de conditions :

  1. orConditionGroup() :
    Les conditions sont combinées avec l'opérateur logique OR. Cela permet de spécifier plusieurs critères de recherche où au moins une des conditions doit être vraie pour qu'un enregistrement soit inclus dans les résultats.
     
  2. andConditionGroup() :
    Les conditions sont combinées avec l'opérateur logique AND. Cela signifie que tous les critères spécifiés dans ce groupe doivent être vrais simultanément pour qu'un enregistrement soit inclus dans les résultats.
// Exemple de requête dans laquelle nous souhaitons récupérer les noeuds avec le terme de taxonomie dont l'id est 5 OU celui dont l'id est 6 // Nous initions la requète $query = \Drupal::entityQuery('node'); $query->accessCheck(TRUE); // Nous créons notre groupe de condtions OR $condition_or = $query->orConditionGroup(); $condition_or->condition("field_tags", "5", "IN"); $condition_or->condition("field_tags", "6", "IN"); // Nous ajoutons notre groupe de conditions OR à la requête $query->condition($condition_or); // Nous executons la requêtes et chargons les noeuds $node_ids = $query->execute(); $nodes = Node::loadMultiple($node_ids);
// Exemple de requête dans laquelle nous souhaitons récupérer les noeuds publiés OU les noeuds avec les terme de taxonomie dont les ids sont 5 ET 6 // Nous initions la requète $query = \Drupal::entityQuery('node'); $query->accessCheck(FALSE); // Nous créons notre groupe de condtions OR $condition_or = $query->orConditionGroup(); // Nous ajoutons notre première conditions $condition_or->condition("status", 1); // Nous créons un deuxième groupe de contions AND que nous ajoutons à notre premier groupe OR $condition_and = $query->andConditionGroup(); $condition_and->condition("field_tags", "5", "IN"); $condition_and->condition("field_tags", "6", "IN"); $condition_or->condition($condition_and); // Nous ajoutons notre groupe de conditions OR à la requête $query->condition($condition_or); // Nous executons la requêtes et chargons les noeuds $node_ids = $query->execute(); $nodes = Node::loadMultiple($node_ids);

La vérification de l'existence d'une valeur

Pour déterminer si un champ de base de données est renseigné ou non, il est possible d'employer deux méthodes distinctes dans le cadre d'une requête :

  1. Utilisation de $query->exists('field_name') : 
    Cette méthode permet de vérifier que le champ nommé field_name contient une valeur. Si une valeur est présente, la condition est satisfaite, ce qui peut être utile pour filtrer les enregistrements dans des requêtes de sélection ou de mise à jour.
     
  2. Utilisation de $query->notExists('field_name') : 
    À l'inverse, cette méthode vérifie que le champ field_name est vide. Aucune valeur ne doit être présente dans le champ pour que la condition soit considérée comme remplie. Cela peut être particulièrement pertinent lorsqu'on souhaite identifier les enregistrements incomplets ou les données manquantes.
// Requête pour récupérer tous les utilisateurs dont le nom de famille n'est pas renseigné // Nous initions la requète $query = \Drupal::entityQuery("user"); $query->accessCheck(FALSE); // Nous ajoutons notre condition $query->notExists("field_last_name"); // Nous executons la requêtes et chargons les utilisateurs $user_ids = $query->execute(); $users = User::loadMultiple($user_ids);

L'utilisation des dates dans les requêtes

Drupal offre la flexibilité de stocker les dates de plusieurs manières, adaptées à différents besoins d'application :

  1. Timestamp (U)
    Ce format stocke la date et l'heure sous forme de timestamp Unix, qui représente le nombre de secondes écoulées depuis le 1er janvier 1970. Ce format est particulièrement utile pour les calculs de durée et la conversion entre différents fuseaux horaires.
     
  2. Date seule (Y-m-d)
    Utilisé pour stocker uniquement la date sans l'heure, ce format est idéal pour les événements ou les échéances qui ne nécessitent pas de précision horaire.
     
  3. Date et heure (Y-m-d\TH:i:s)
    Ce format inclut à la fois la date et l'heure, et est adapté aux enregistrements nécessitant une précision temporelle, comme les horaires de rendez-vous ou les enregistrements d'événements.

Cette flexibilité nécessite d'adapter les requêtes en fonction du format de stockage choisi.

// Exemple de requête sur le cas du format timestamp. // Nous souhaitons récupérer tous les utilisateurs créés avant le 1er Janvier 2024 // On va créer notre date $date = DrupalDateTime::createFromFormat("Y-m-d H:i", "2023-12-31 23:59"); $dateToCompare = $date->setTimezone(new DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE))->format("U"); // Nous initions la requète $query = \Drupal::entityQuery("user"); $query->accessCheck(FALSE); // Nous ajoutons notre condition $query->condition("created", $dateToCompare, "<"); // Nous executons la requêtes et chargons les utilisateurs $user_ids = $query->execute(); $users = User::loadMultiple($user_ids);
// Exemple de requête sur le cas du format date. // Nous souhaitons récupérer tous les événements qui ont lieu entre 15 Janvier 2024 et le 31 Janvier 2024 // Nous initions la requète $query = \Drupal::entityQuery("event"); $query->accessCheck(FALSE); // Nous ajoutons nos conditions $query->condition("field_date", "2024-01-15", ">="); $query->condition("field_date", "2024-01-31", "<="); // Nous executons la requêtes et chargons les événements $event_ids = $query->execute(); $events = User::loadMultiple($event_ids);
// Exemple de requête sur le cas du format date et heure. // Nous souhaitons récupérer toutes les réseervations qui démarrent ou se terminent entre 15 Janvier 2024 à miniut et le 31 Janvier 2024 à minuit // Nous définissons nos dates $start = "2024-01-15"; $startDateTime = DrupalDateTime::createFromFormat("Y-m-d H:i", $start . " 00:00"); $startToCompare = $startDateTime->setTimezone(new DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE))->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); $end = "2024-01-31"; $endDateTime = DrupalDateTime::createFromFormat("Y-m-d H:i", $end . " 23:59"); $endToCompare = $endDateTime->setTimezone(new DateTimeZone(DateTimeItemInterface::STORAGE_TIMEZONE))->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT); // Nous initions la requète $query = Drupal::entityQuery("booking"); // Nous ajoutons nos conditions $query->condition("field_start_date", $endToCompare, "<"); $query->condition("field_end_date", $startToCompare, ">"); // Nous executons la requêtes et chargons les réservations $booking_ids = $query->execute(); $bookings = Booking::loadMultiple($booking_ids);

Les conditions sur un champ d'une entité référencée

Si votre entité Drupal contient une référence à une autre entité, il est non seulement possible mais également efficace de créer des conditions basées sur les champs de l'entité référencée. 

Cette méthode permet de réaliser des requêtes complexes et ciblées. Par exemple, vous pouvez filtrer les nœuds d'un type spécifique dont l'entité référencée remplit certaines conditions, comme une plage de dates ou un statut particulier.

// Nous initions la requète $query = \Drupal::entityQuery("node"); $query->accessCheck(TRUE); // Nous ajouter notre condition pour récupérer les noeuds dont les utilisateurs sont inactifs $query->condition("uid.entity:user.status", "1"); // Nous executons la requêtes et chargons les noeuds $node_ids = $query->execute(); $nodes = Node::loadMultiple($node_ids);

Optimisation de l'affichage du résultat

La gestion du tri

Drupal fournit une méthode pour implémenter le tri dans les requêtes permettant de spécifier l'ordre des résultats (ascendant par défaut) :

$query->sort("field_name", "ASC/DESC");

La gestion de la pagination

Drupal simplifie la pagination des résultats de requêtes grâce à la méthode $query->range().

Dans l'exemple $query->range(0, 20);, les vingt premiers résultats seront affichés. 

Le second exemple, $query->range(10, 20);, montre comment sauter les dix premiers résultats et afficher les vingt suivants. Cela permet de commencer l'affichage à partir du onzième résultat, ce qui est pratique pour naviguer à travers des séries de données sans avoir à charger toutes les entrées précédentes.

L'affichage du nombre de résultat

Pour quantifier les résultats d'une requête, Drupal utilise :

$count_results = $query->count()->execute();

Pour conclure

En approfondissant votre compréhension et votre utilisation des EntityQuery, vous pouvez débloquer de nouvelles possibilités pour vos projets Drupal, en rendant vos sites plus interactifs, dynamiques et personnalisés. Les exemples concrets fournis ici devraient servir de tremplin pour explorer encore plus loin et affiner vos compétences. 

La logique vous mènera d'un point A à un point B. L'imagination vous emmènera partout.

Albert Einstein