Drupal 8, 9 et 10 - Les EntityQuery par l'exemple

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.

//On commence par donner le type d'entité que l'on souhaite « requêter »
$query = \Drupal::entityQuery('user'); 
//On ajoute une condition, ici « status = 1 » pour ne récupérer que les utilisateurs actifs
$query->condition('status', 1); 
//On active ou non la vérification d'accès à l'entité
$query->accessCheck(TRUE); 
//On lance la requête et récupère les ids des utilisateurs.
$users_ids = $query->execute(); 
//on charge les utilisateurs en question
$users = User::loadMultiple($users_ids);

Note sur l'access check

Depuis drupal 10, les query retournent une erreur si le contrôle d'accès n'est pas explicitement déclaré, cf https://www.drupal.org/node/3201242 .

Il est nécessaire d'ajouter l'appel à la méthode accessCheck en lui passant TRUE ou FALSE suivant si l'on veut activer le comportement.

Pour faire simple, si on fait une requêtes sur les noeuds, en ajoutant accessCheck(TRUE), uniquement les nœuds publiés seront retournés, alors qu'avec accessCheck(FALSE) l'ensemble des noeuds seront retournés.

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

// Requête sur les utilisateurs
$query = \Drupal::entityQuery('user');

// Requête sur les noeuds 
$query = \Drupal::entityQuery('node');

// Requête sur les terme de taxonomie 
$query = \Drupal::entityQuery('taxonomy_term'); 

Les conditions


$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 » :

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

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

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

mais on peut évidement le spécifier :

// uid "différent de" 1
$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 :

$interests = [127, 128, 27];
$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$profils_similaires = $query->execute();

$users = User::loadMultiple($profils_similaires);

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

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

Plus propre en utilisant db_like (\Drupal\Core\Database\Database::getConnection()->escapeLike en drupal 10)  qui fait un échappement

$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', 'tags');
$query->condition('name', '%' . \Drupal\Core\Database\Database::getConnection()->escapeLike($keyword) . '%', 'like');
$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)

// Tester que le champ field_name soit renseigné :
// Équivalent à where field_name IS NOT NULL
$query->exists('field_name');

// Tester que le champ field_name ne soit pas renseigné :
// Équivalent à where field_name IS NULL
$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 :

$query = \Drupal::entityQuery('user');
$query->condition('field_interests', $interests, 'IN');
$query->condition('field_birthday', $min_date->format('Y-m-d'), '<');
$query->condition('field_birthday', $max_date->format('Y-m-d'), '>');
$profils_similaires = $query->execute();

$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)

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);
$condition_or->condition('field_interests', $interests, 'IN');

$query->condition($condition_or);

$profils_similaires = $query->execute();

$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 :

$query = \Drupal::entityQuery('user');

$condition_or = $query->orConditionGroup();
$condition_or->condition('field_pinned',1);

$condition_and = $query->andConditionGroup();

$condition_and->condition('field_interests', $interests, 'IN');
$condition_and->condition('field_birthday', $min_date->format('Y-m-d'), '<');
$condition_and->condition('field_birthday', $max_date->format('Y-m-d'), '>');

$condition_or->condition($condition_and);

$query->condition($condition_or);

$profils_similaires = $query->execute();

$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 :

$query = \Drupal::entityQuery('photo');
$query->condition('creator.entity:user.status', 12);
$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

// 20 résultats sans en passer aucun (0)
$query->range(0, 20);

// 20 résultats en passant les 10 premiers (0)
$query->range(10, 20);

Gestion du tri

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

ou en précisant le sens :

// Tri sur le champ field_birthday par ordre croissant
$query->sort('field_birthday', 'ASC');

// Tri sur le champ field_birthday par ordre décroissant
$query->sort('field_birthday', 'DESC');

Compter le nombre de résultats

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

 

Contenus en rapport

Drupal 8 & Drupal 9 - Entity Query - Ajouter une condition sur une colonne spécifique

Dans le cadre d'une EntityQuery, il peut être nécessaire parfois de faire une requête sur une colonne spécifique de notre table, autre que le traditionnel « value ».

Rien de bien compliqué, il faut alors le spécifier dans le nom du champ sur lequel on ajoute une condition.

Commentaires

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');

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

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.

Pour cela, le plus simple est de faire :

MyEntityType::loadMultiple()

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.

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

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 !

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

 

//récupération de tous les résultats
$users_ids = $query->execute(); 

//on ne garde que le dernier élément du tableau
$user_id = array_pop($users_ids);

//On charge cet élément
$users = User::load($user_id);

 

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

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é
CAPTCHA
Désolé, pour ça, mais c'est le seul moyen pour éviter le spam...