Drupal 8 - Les EntityQuery par l'exemple

Posté le Mardi 26 septembre 2017 - 08:24
Dernière mise à jour le Jeudi 10 septembre 2020 - 17:01
Entity Query

Remplacement des Entity Field Query en drupal 7, les Entity Query permettent d'effectuer des requêtes sur nos types d'entités, (custom ou non) selon leurs propriétés ou leurs champs (fields).

Elles sont une bonne solution pour faire des queries même avancées, sans avoir à faire des jointures à en perdre la tête.

Un exemple simple

Récupération et chargement de l'ensemble des utilisateurs activés.

  1. //On commence par donner le type d'entité que l'on souhaite « requêter »
  2. $query = \Drupal::entityQuery('user');
  3. //On ajoute une condition, ici « status = 1 » pour ne récupérer que les utilisateurs actifs
  4. $query->condition('status', 1);
  5. //On lance la requête et récupère les ids des utilisateurs.
  6. $users_ids = $query->execute();
  7. //on charge les utilisateurs en question
  8. $users = User::loadMultiple($users_ids);

Que peut-on peut requêter ?

Sous drupal 8, tout est entité (ou presque), on peut ainsi faire des requêtes sur :

  • Les noeuds
  • Les blocs
  • Les utilisateurs
  • Les menus
  • Les éléments de menus
  • Nos types d'entités custom
  • ...

Bref, beaucoup de chose !

La déclaration du type d'entité se fait lors de la génération de la requête :

Pour requêter les utilisateurs

  1. // Requête sur les utilisateurs
  2. $query = \Drupal::entityQuery('user');
  3.  
  4. // Requête sur les noeuds
  5. $query = \Drupal::entityQuery('node');
  6.  
  7. // Requête sur les terme de taxonomie
  8. $query = \Drupal::entityQuery('taxonomy_term');

Les conditions

  1. $query->condition(champ_ou_propriété, valeur, opérateur);

Le champ sur lequel on fait une condition peut -être soit une propriété (nid, changed, created, title, name...) ou bien un champ « field » (field_tags, field_image...)

Exemple en requetant les noeuds de type « produit » :

  1. $query = \Drupal::entityQuery('node');
  2. $query->condition('type', 'produit');

Par défaut l'opérateur est : = (égal)

  1. // uid == 1
  2. $query->condition('uid', 1);

mais on peut évidement le spécifier :

  1. // uid "différent de" 1
  2. $query->condition('uid', $user->id(), '<>');

On peut utiliser le IN, afin de, par exemple récupérer et charger tous les users en fonction d'un field "interest" qui fait référence à des termes de taxonomies :

  1. $interests = [127, 128, 27];
  2. $query = \Drupal::entityQuery('user');
  3. $query->condition('field_interests', $interests, 'IN');
  4. $profils_similaires = $query->execute();
  5.  
  6. $users = User::loadMultiple($profils_similaires);

Avec un "LIKE", afin de tester le même département :

  1. // si $code_postal = "123456"
  2. // field_adresse_zipcode LIKE "12%"
  3. $query->condition('field_adresse_zipcode', substr($code_postal, 0, 2).'%', 'LIKE');

Plus propre en utilisant db_like qui fait un échappement

  1. $query = \Drupal::entityQuery('taxonomy_term');
  2. $query->condition('vid', 'tags');
  3. $query->condition('name', '%' . db_like($keyword) . '%', 'like');
  4. $terms = Term::loadMultiple($query->execute());

Tester si un champ est vide ou non vide (is null / is not null)

(Comme signalé par Christophe Caron dans les commentaires)

  1. // Tester que le champ field_name soit renseigné :
  2. // Équivalent à where field_name IS NOT NULL
  3. $query->exists('field_name');
  4.  
  5. // Tester que le champ field_name ne soit pas renseigné :
  6. // Équivalent à where field_name IS NULL
  7. $query->notExists('field_name');

Conditions multiples

Aussi on peut ajouter plusieurs conditions, ici je fais un test sur la date de naissance, qu'elle soit bien comprise entre $min_date & $max_date :

  1. $query = \Drupal::entityQuery('user');
  2. $query->condition('field_interests', $interests, 'IN');
  3. $query->condition('field_birthday', $min_date->format('Y-m-d'), '<');
  4. $query->condition('field_birthday', $max_date->format('Y-m-d'), '>');
  5. $profils_similaires = $query->execute();
  6.  
  7. $users = User::loadMultiple($profils_similaires);

Avec des conditions Or

On peut utiliser des conditions logiques « OR », ici je veux récuperer les utilisateurs qui ont les mêmes centres d'intérêts (field_interests) OU qui sont manuellement mis en avant (field_pinned)

  1. $query = \Drupal::entityQuery('user');
  2.  
  3. $condition_or = $query->orConditionGroup();
  4. $condition_or->condition('field_pinned',1);
  5. $condition_or->condition('field_interests', $interests, 'IN');
  6.  
  7. $query->condition($condition_or);
  8.  
  9. $profils_similaires = $query->execute();
  10.  
  11. $users = User::loadMultiple($profils_similaires);

Avec des conditions OR et AND

On peut faire des conditions un peu plus balaises, avec ici donc : soit les utilisateurs mis en avant (field_pinned) OU (qui ont les même centres d'intérêts (field_interest) ET qui sont nés après $max_date ET avant $min_date :

  1. $query = \Drupal::entityQuery('user');
  2.  
  3. $condition_or = $query->orConditionGroup();
  4. $condition_or->condition('field_pinned',1);
  5.  
  6. $condition_and = $query->andConditionGroup();
  7.  
  8. $condition_and->condition('field_interests', $interests, 'IN');
  9. $condition_and->condition('field_birthday', $min_date->format('Y-m-d'), '<');
  10. $condition_and->condition('field_birthday', $max_date->format('Y-m-d'), '>');
  11.  
  12. $condition_or->condition($condition_and);
  13.  
  14. $query->condition($condition_or);
  15.  
  16. $profils_similaires = $query->execute();
  17.  
  18. $users = User::loadMultiple($profils_similaires);

Condition sur un champ d'une entité liée

Si votre noeud ou entité contient une référence à une autre entité, il est possible de faire une condition sur une champ de cette entité.

Exemple :

  1. $query = \Drupal::entityQuery('photo');
  2. $query->condition('creator.entity:user.status', 12);
  3. $ids = $query->execute();

Pour plus de détails là dessus, voir ici : https://kgaut.net/snippets/2020/drupal-8-et-drupal-9-entityquery-faire-…

Gestion de la pagination et du nombre de résultats

  1. // 20 résultats sans en passer aucun (0)
  2. $query->range(0, 20);
  3.  
  4. // 20 résultats en passant les 10 premiers (0)
  5. $query->range(10, 20);

Gestion du tri

  1. // Tri par date de création (ASC par défaut)
  2. $query->sort('created');

ou en précisant le sens :

  1. // Tri sur le champ field_birthday par ordre croissant
  2. $query->sort('field_birthday', 'ASC');
  3.  
  4. // Tri sur le champ field_birthday par ordre décroissant
  5. $query->sort('field_birthday', 'DESC');

Compter le nombre de résultats

  1. $query = \Drupal::entityQuery('node');
  2. $query->condition('type', 'produit');
  3. $nb_resultats = $query->count()->execute();

 

Aller plus loin ?

 

Comments

Christophe Caron

Posté le Lundi 8 janvier 2018 - 10:37

Bravo pour cet article très précis, j'ajouterai :

Vérifier qu'un champ n'est pas vide :
$query->exists('field_name');

Vérifier qu'un champ est vide :
$query->notExists('field_name');

kgaut

Posté le Vendredi 19 janvier 2018 - 18:11

Merci, j'ai ajouté ça à l'article !

Laurent

Posté le Mercredi 16 mai 2018 - 12:47

Bonjour,

Comment peut-on récupérer la liste complète (sans condition) d'un type d'entité ?
J'ai essayé
Drupal::entityQuery('my_entity_type')->execute();
Mais ça ne me ramène rien.

kgaut

Posté le Mercredi 16 mai 2018 - 13:02

Pour cela, le plus simple est de faire :

MyEntityType::loadMultiple()

Sym

Posté le Jeudi 21 juin 2018 - 12:19

Bonjour,
Merci pour ces exemples très clairs :)
Je me demande par contre comment trier sur deux champs différents . Par exemple : par position (integer) puis par date de creation.

mnlk

Posté le Vendredi 31 mai 2019 - 22:38

Bonjour, j'ai un champs qui fait référence a un terme de taxonomie de deux valeurs "63, 64" j'aimerais récupérer sois les node contenant 63 ou 64 et ceux qui possèdent les deux à la fois

Kpesenti

Posté le Mercredi 27 novembre 2019 - 09:50

Bonjour,
Merci pour cet article ! Auriez vous un exemple pour ne sélectionner qu'un seul object? Faire ::load au lieu du loadmultiple en gros, mais je ne suis pas très à l'aise avec les ids retournés. Merci !

kgaut

Posté le Mercredi 27 novembre 2019 - 09:54

Si on sait que notre requête ne retourne qu'un seul élément alors on peut faire :

 

  1. //récupération de tous les résultats
  2. $users_ids = $query->execute();
  3.  
  4. //on ne garde que le dernier élément du tableau
  5. $user_id = array_pop($users_ids);
  6.  
  7. //On charge cet élément
  8. $users = User::load($user_id);

 

Kévin

Posté le Samedi 11 avril 2020 - 00:58

Bonjour,

J'utilise le module Group de Drupal 8.
J'ai un group société qui peut par exemple contenir des offres d'emplois (un type de contenu personnalisé)
Comment faire pour compter le nombre d'offre d'emploi pour le groupe courrant ?

Cordialement,
Kévin

Rvs

Posté le Vendredi 11 septembre 2020 - 00:54

Hi ! Je bloque actuellement sur une requête et votre tutoriel m'a déjà beaucoup aidé !
Je dois récupérer le prénom et nom d'un utilisateur authentifié pour les passer en objet de mail ... C'est assez bizarre, mais c'est une demande client ^^.. Quand j'utilise votre code et que j'execute un
var_dump($users);
je récupère un tableau énorme. J'ai essayé d'affiner en rentrant des conditions comme
$query->condition('uid', 'User::load(\Drupal::currentUser()->id())');
seulement je n'arrive pas à récupérer la valeur précise d'une colonne .. Auriez-vous une astuce ?
Merci

Ajouter un commentaire

Ne sera pas publié

HTML restreint

  • Balises HTML autorisées : <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Les adresses de pages web et les adresses courriel se transforment en liens automatiquement.
CAPTCHA Désolé, pour ça, mais c'est le seul moyen pour éviter le spam...