logo

Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll
21/06/2025, by Ivan

Les collections sont des listes de ressources. Dans un site découplé, ce sont celles que vous utilisez pour créer des éléments comme une liste de « Nouveau contenu » ou une section « Mon contenu » côté client.

Cependant, lorsque vous effectuez une requête non filtrée vers un point d’accès de collection comme /jsonapi/node/article, vous obtiendrez simplement tous les articles que vous êtes autorisé à voir.

Sans filtres, vous ne pouvez pas obtenir uniquement vos articles ou uniquement des articles sur les lamas.

Ce guide vous apprendra à construire des filtres comme un pro.

Démarrage rapide

Le filtre le plus simple et le plus courant est un filtre clé-valeur :

?filter[field_name]=value&filter[field_other]=value

Cela correspond à toutes les ressources où "field_name" est égal à "value" et "field_other" est égal à "value".

Pour tout le reste, continuez à lire !

Résumé


Le module JSON:API propose certains des filtres les plus puissants et riches en fonctionnalités. Toute cette puissance s’accompagne cependant d’une certaine courbe d’apprentissage.

À la fin de cet article, vous serez capable de faire des requêtes complexes et de résoudre des problèmes tels que « comment obtenir une liste d’articles d’un auteur sur les lamas ou sur le membre le plus rapide du règne animal, le faucon pèlerin ? »

Nous allons commencer par les bases, puis vous montrer quelques raccourcis pour écrire des filtres plus rapidement et de manière moins verbeuse. Enfin, nous verrons plusieurs exemples de filtres tirés du monde réel.

Si vous connaissez déjà Drupal, vous avez probablement déjà utilisé le module Views pour ce genre de choses. Contrairement au module REST fourni avec Drupal Core, JSON:API n’exporte pas les résultats de Views. Les collections sont la solution API-First JSON:API pour remplacer les « affichages REST » exportés dans Views.

Construction des filtres


Les blocs fondamentaux des filtres JSON:API sont les conditions et les groupes. Les conditions affirment qu’une chose est vraie et les groupes vous permettent de composer ces affirmations en ensembles logiques pour former des groupes de conditions plus importants. Ces ensembles peuvent être imbriqués pour réaliser des requêtes très fines. Vous pouvez imaginer ces ensembles imbriqués comme un arbre :

Représentation conventionnelle :

a( b() && c( d() || e() ) )

Représentation en arbre :

   a
  / \
 b & c
    / \
   d | e

Dans les deux cas :

"d" et "e" sont membres de "c" dans un groupe OU.
"b" et "c" sont membres de "a" dans un groupe ET.

Alors, que contient une condition ?

Entrons dans la logique 🖖. Une condition vous indique une valeur VRAI ou FAUX concernant une ressource et une assertion que vous faites à son sujet, comme « cette entité a-t-elle été créée par un utilisateur particulier ? » Quand la condition est FAUSSE pour une ressource, cette ressource n’est pas incluse dans la collection.

Une condition a 3 parties principales : un chemin, un opérateur et une valeur.

  • Un « chemin » identifie un champ sur une ressource.
  • Un « opérateur » est une méthode de comparaison.
  • Une « valeur » est ce contre quoi vous comparez.

En pseudo-code, une condition ressemble à ceci :

($field !== 'space')

Où :

  1. $field est le champ de la ressource identifié par son « chemin ».
  2. l’« opérateur » est !==.
  3. la « valeur » est la chaîne 'space'.

Dans le module JSON:API, on ne peut pas faire aussi simple car cela doit fonctionner dans une chaîne de requête URL. Pour cela, chaque condition est représentée par des paires clé/valeur.

Si nous filtrons sur le prénom d’un utilisateur, une condition pourrait ressembler à ceci :

?filter[a-label][condition][path]=field_first_name
&filter[a-label][condition][operator]=%3D  <- symbole "=" encodé
&filter[a-label][condition][value]=Janis

Notez que nous avons mis un identifiant dans le premier crochet. Cela aurait pu être b-label ou this_is_my_super_awesome_label ou même un entier comme 666 🤘😅. L’important est que chaque condition ou groupe ait un identifiant.

Mais que faire si nous avons beaucoup de Janis dans le système ?

Ajoutons un autre filtre pour n’obtenir que les Janis dont le nom commence par « J » :

?filter[first-name-filter][condition][path]=field_first_name
&filter[first-name-filter][condition][operator]=%3D  <- "=" encodé
&filter[first-name-filter][condition][value]=Janis

&filter[last-name-filter][condition][path]=field_last_name
&filter[last-name-filter][condition][operator]=STARTS_WITH
&filter[last-name-filter][condition][value]=J

Peut-être que le pluriel de Janis est « Janii » 🤔...

Il existe bien plus d’opérateurs que juste = et STARTS_WITH. Voici la liste complète tirée directement du code JSON:API :

\Drupal\jsonapi\Query\EntityCondition::$allowedOperators = [
  '=', '<>',
  '>', '>=', '<', '<=',
  'STARTS_WITH', 'CONTAINS', 'ENDS_WITH',
  'IN', 'NOT IN',
  'BETWEEN', 'NOT BETWEEN',
  'IS NULL', 'IS NOT NULL',
];

Les opérateurs symboliques doivent être encodés pour URL. Vous pouvez utiliser la fonction PHP urlencode() pour obtenir le bon encodage.

Groupes de conditions


Nous savons maintenant construire des conditions, mais pas encore des groupes de conditions. Comment construire un arbre comme celui montré plus haut ?

Pour cela, il nous faut un « groupe ». Un groupe est un ensemble de conditions jointes par une « conjonction ». Tous les groupes ont des conjonctions, qui sont soit ET soit OU.

Notre filtre est maintenant un peu trop spécifique ! Disons que nous voulons trouver tous les utilisateurs dont le nom commence par « J » et qui ont soit le prénom « Janis » ou le prénom « Joan ».

Pour cela, nous ajoutons un groupe :

?filter[rock-group][group][conjunction]=OR

Puis, nous devons assigner nos filtres à ce nouveau groupe.

Pour cela, nous ajoutons une clé memberOf. Chaque condition et groupe peut avoir une clé memberOf.

Astuce : Les groupes peuvent avoir une clé memberOf comme les conditions, ce qui signifie que nous pouvons avoir des groupes de groupes 🤯 !

Note : Tout filtre sans clé memberOf est considéré comme faisant partie d’un groupe « racine » avec une conjonction ET.

Voici tout cela réuni :

?filter[rock-group][group][conjunction]=OR

&filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][operator]=%3D
&filter[janis-filter][condition][value]=Janis
&filter[janis-filter][condition][memberOf]=rock-group

&filter[joan-filter][condition][path]=field_first_name
&filter[joan-filter][condition][operator]=%3D
&filter[joan-filter][condition][value]=Joan
&filter[joan-filter][condition][memberOf]=rock-group

&filter[last-name-filter][condition][path]=field_last_name
&filter[last-name-filter][condition][operator]=STARTS_WITH
&filter[last-name-filter][condition][value]=J

Ça vous rappelle quelque chose ?

Ça devrait, nous l’avons vu plus haut sous forme d’arbre :

   a   a = root-and-group
  / \
 /   \    b = last-name-filter
b     c   c = rock-group
     / \
    /   \    d = janis-filter
   d     e   e = joan-filter

Vous pouvez imbriquer ces groupes aussi profondément que vous le souhaitez.

Chemins (Paths)

Les conditions ont une dernière fonctionnalité : les « chemins »

Les chemins permettent de filtrer en fonction des valeurs des relations.

Jusqu’ici, nous avons simplement filtré sur les champs hypothétiques field_first_name et field_last_name de la ressource utilisateur.

Imaginons maintenant que nous voulions filtrer par le nom de la carrière d’un utilisateur, où les types de carrière sont stockés comme ressource séparée. Nous pourrions ajouter un filtre comme ceci :

?filter[career][condition][path]=field_career.name
&filter[career][condition][operator]=%3D
&filter[career][condition][value]=Rockstar

Les chemins utilisent une « notation pointée » pour parcourir les relations.

Si une ressource a une relation, vous pouvez filtrer dessus en concaténant le nom du champ relationnel et le nom du champ de la relation avec un . (point).

Vous pouvez même filtrer sur les relations des relations (et ainsi de suite) en ajoutant simplement des noms de champs et des points.

Astuce : Vous pouvez filtrer sur un index spécifique d’une relation en mettant un entier non négatif dans le chemin. Ainsi, le chemin some_relationship.1.some_attribute filtrera uniquement sur la 2ᵉ ressource liée.

Astuce : Vous pouvez filtrer sur des sous-propriétés d’un champ. Par exemple, un chemin comme field_phone.country_code fonctionnera même si field_phone n’est pas une relation.

Astuce : Pour filtrer sur des propriétés de configuration, vous pouvez utiliser un astérisque (*) pour représenter une partie quelconque du chemin. Par exemple, /jsonapi/field_config/field_config?filter[dependencies.config.*]=comment.type.comment correspondra à toutes les configurations de champ où ["attributes"]["dependencies"]["config"] (un tableau indexé) contient la valeur « comment.type.comment ».

Raccourcis


C’est beaucoup de caractères à taper. La plupart du temps, vous n’avez pas besoin de filtres aussi compliqués, et dans ces cas-là, le module JSON:API propose quelques « raccourcis » pour vous aider à écrire des filtres plus rapidement.

Quand l’opérateur est =, vous n’êtes pas obligé de le mentionner. Il est simplement supposé. Ainsi :

?filter[a-label][condition][path]=field_first_name
&filter[a-label][condition][operator]=%3D  <- symbole "=" encodé
&filter[a-label][condition][value]=Janis

devient

?filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][value]=Janis

Il est aussi rare que vous deviez filtrer deux fois sur le même champ (même si c’est possible). Donc, quand l’opérateur est = et que vous n’avez pas besoin de filtrer deux fois sur le même champ, le chemin peut être l’identifiant. Ainsi :

?filter[janis-filter][condition][path]=field_first_name
&filter[janis-filter][condition][value]=Janis

devient

?filter[field_first_name][value]=Janis

Cette valeur value est un peu encombrante. C’est pourquoi vous pouvez réduire les égalités les plus simples à une forme clé-valeur :

?filter[field_first_name]=Janis

Filtres et contrôle d’accès


Un avertissement d’abord : ne confondez pas filtres et contrôle d’accès. Le fait d’avoir écrit un filtre pour exclure quelque chose qu’un utilisateur ne devrait pas voir ne garantit pas que ce contenu est inaccessible. Effectuez toujours des contrôles d’accès côté serveur.

Avec ce grand avertissement, parlons de l’utilisation des filtres pour compléter le contrôle d’accès. Pour améliorer les performances, vous devriez filtrer ce que vos utilisateurs ne pourront pas voir. La demande la plus fréquente dans les issues JSON:API peut être résolue par cette simple astuce !

Si vous savez que vos utilisateurs ne peuvent pas voir le contenu non publié, ajoutez le filtre suivant :

?filter[status][value]=1

Avec cette méthode, vous réduirez le nombre de requêtes inutiles. En effet, JSON:API ne renvoie pas les données des ressources auxquelles un utilisateur n’a pas accès. Vous pouvez vérifier quelles ressources ont été affectées en inspectant la section meta.errors du document JSON:API.

Donc, faites de votre mieux pour filtrer les ressources inaccessibles en amont.

Exemples de filtres


1. Obtenir uniquement les nœuds publiés

Un cas très courant est de ne charger que les nœuds publiés. C’est un filtre très simple à ajouter.

COURT
filter[status][value]=1

NORMAL
filter[status-filter][condition][path]=status
filter[status-filter][condition][value]=1

2. Obtenir des nœuds par une valeur de référence d’entité

Une stratégie courante est de filtrer le contenu par une référence d’entité.

COURT
filter[uid.id][value]=BB09E2CD-9487-44BC-B219-3DC03D6820CD

NORMAL
filter[author-filter][condition][path]=uid.id
filter[author-filter][condition][value]=BB09E2CD-9487-44BC-B219-3DC03D6820CD

Pour respecter pleinement la spécification JSON API, alors que Drupal utilise en interne la propriété uuid, JSON API utilise id à la place.

Depuis Drupal 9.3, il est possible de filtrer aussi sur la propriété target_id au lieu de seulement uuid.

COURT
filter[field_tags.meta.drupal_internal__target_id]=1

NORMAL
filter[name-filter][condition][path]=field_tags.meta.drupal_internal__target_id
filter[name-filter][condition][value]=1

3. Filtres imbriqués : Obtenir les nœuds créés par l’utilisateur admin

Il est possible de filtrer sur des champs d’entités référencées comme l’utilisateur, les champs de taxonomie ou tout champ de référence d’entité. Vous pouvez facilement le faire avec la notation suivante : champ_de_référence.champ_imbriqué. Ici, le champ de référence est uid pour l’utilisateur et name qui est un champ de l’entité utilisateur.

COURT
filter[uid.name][value]=admin

NORMAL
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][value]=admin

4. Filtrer avec des tableaux : Obtenir les nœuds créés par les utilisateurs [admin, john].

Vous pouvez fournir plusieurs valeurs à un filtre. À côté des clés field et value, vous pouvez ajouter un opérateur. Habituellement c’est « = » mais vous pouvez aussi utiliser « IN », « NOT IN », « > », « < », « <> », « BETWEEN ».

Pour cet exemple, nous utilisons l’opérateur IN. Notez que j’ai ajouté deux crochets derrière la valeur pour en faire un tableau.

NORMAL
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][operator]=IN
filter[name-filter][condition][value][1]=admin
filter[name-filter][condition][value][2]=john

Astuce : lorsque vous utilisez des crochets pour des filtres à valeurs multiples, n’utilisez pas simplement des crochets vides pour une nouvelle valeur.
Bien que cela fonctionne dans l’URL, Guzzle et d’autres clients HTTP ne créeront qu’une seule valeur car la clé du tableau sera la même et écrasera la précédente. Il est préférable d’utiliser un index pour créer des éléments uniques dans le tableau.

5. Regroupement des filtres : Obtenir les nœuds publiés et créés par admin.

Maintenant combinons certains exemples ci-dessus et créons le scénario suivant :
WHERE user.name = admin AND node.status = 1;

filter[and-group][group][conjunction]=AND
filter[name-filter][condition][path]=uid.name
filter[name-filter][condition][value]=admin
filter[name-filter][condition][memberOf]=and-group
filter[status-filter][condition][path]=status
filter[status-filter][condition][value]=1
filter[status-filter][condition][memberOf]=and-group

Vous n’êtes pas obligé d’ajouter ce groupe and-group mais c’est souvent plus clair ainsi.

6. Regroupement des groupes filtrés : Obtenir les nœuds promus ou épinglés et créés par admin

Comme mentionné dans la section sur les groupes, vous pouvez imbriquer des groupes dans d’autres groupes.
WHERE (user.name = admin) AND (node.sticky = 1 OR node.promoted = 1)

Pour cela, mettez sticky et promoted dans un groupe avec conjonction OR. Créez un groupe avec conjonction AND et mettez dedans le filtre admin et le groupe OR promoted/sticky.

# Créer un groupe AND et un groupe OR
filter[and-group][group][conjunction]=AND
filter[or-group][group][conjunction]=OR

# Mettre le groupe OR dans le groupe AND
filter[or-group][group][memberOf]=and-group

# Créer le filtre admin et le mettre dans le groupe AND
filter[admin-filter][condition][path]=uid.name
filter[admin-filter][condition][value]=admin
filter[admin-filter][condition][memberOf]=and-group

# Créer le filtre sticky et le mettre dans le groupe OR
filter[sticky-filter][condition][path]=sticky
filter[sticky-filter][condition][value]=1
filter[sticky-filter][condition][memberOf]=or-group

# Créer le filtre promote et le mettre dans le groupe OR
filter[promote-filter][condition][path]=promote
filter[promote-filter][condition][value]=1
filter[promote-filter][condition][memberOf]=or-group

7. Filtrer les nœuds dont le titre CONTIENT "Foo"

COURT
filter[title][operator]=CONTAINS&filter[title][value]=Foo

NORMAL
filter[title-filter][condition][path]=title
filter[title-filter][condition][operator]=CONTAINS
filter[title-filter][condition][value]=Foo

8. Filtrer par champs complexes non standards (ex. champ adresse)

FILTRER PAR LOCALITÉ
filter[field_address][condition][path]=field_address.locality
filter[field_address][condition][value]=Mordor

FILTRER PAR LIGNE D’ADRESSE
filter[address][condition][path]=field_address.address_line1
filter[address][condition][value]=Rings Street

9. Filtrage sur les valeurs de termes de taxonomie (ex. tags)

Pour filtrer, vous devez utiliser le nom machine du vocabulaire et le champ présent sur votre nœud.

filter[taxonomy_term--tags][condition][path]=field_tags.name
filter[taxonomy_term--tags][condition][operator]=IN
filter[taxonomy_term--tags][condition][value][]=tagname

10. Filtrage sur Date (Date seule, sans heure)

Les dates sont filtrables. Passez une chaîne de temps conforme au format ISO-8601.

Cet exemple est pour un champ Date configuré pour ne contenir que la date (sans heure).

filter[datefilter][condition][path]=field_test_date
filter[datefilter][condition][operator]=%3D
filter[datefilter][condition][value]=2019-06-27

Cet exemple est pour un champ Date qui supporte date et heure.

filter[datefilter][condition][path]=field_test_date
filter[datefilter][condition][operator]=%3D
filter[datefilter][condition][value]=2019-06-27T16%3A00%3A00

Notez que les champs timestamp (comme created ou changed) doivent actuellement utiliser un timestamp pour filtrer :

filter[recent][condition][path]=created
filter[recent][condition][operator]=%3D
filter[recent][condition][value]=1591627496

11. Filtrage sur champs tableau vides

Cet exemple concerne un champ Cases à cocher/Boutons radio sans valeur sélectionnée. Imaginez un champ checkbox. Vous voulez obtenir tous les nœuds qui n’ont pas cette valeur cochée. Quand elle est cochée, l’API JSON renvoie un tableau :

"my_field":["checked"] 

Quand elle est décochée, l’API JSON renvoie un tableau vide :

"my_field": [] 

Pour obtenir tous les champs décochés, vous devez utiliser IS NULL sur le tableau comme suit (sans valeur) :

filter[my-filter][condition][path]=my_field
filter[my-filter][condition][operator]=IS NULL

Article tiré de la documentation Drupal.