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
04/09/2025, by Ivan

Collecties zijn overzichten van resources. In een gedecouplede site gebruik je ze aan de client-side om dingen te maken zoals een lijst “Nieuwe inhoud” of een sectie “Mijn inhoud”.

Maar als je een ongefilterd verzoek doet naar een collectie-endpoint zoals /jsonapi/node/article, krijg je gewoon alle artikelen die je mag zien.

Zonder filters kun je niet alleen jouw artikelen ophalen of alleen artikelen over lama’s.

Deze handleiding leert je hoe je filters als een pro opbouwt.

Snelstart

Het eenvoudigste, meest voorkomende filter is een key-value-filter:

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

Dit komt overeen met alle resources waarbij “field_name” gelijk is aan “value” en “field_other” gelijk is aan “value”.

Lees verder voor alles daarbuiten!

Samenvatting


De JSON:API-module heeft een van de meest robuuste en rijk uitgeruste filtermogelijkheden die er zijn. Al die kracht brengt echter wel een leercurve met zich mee.

Tegen het einde van dit artikel kun je complexe queries maken en redeneren over problemen die je kunt tegenkomen, zoals “hoe krijg ik een lijst met artikelen van een auteur over lama’s of het snelste lid van het dierenrijk, de slechtvalk?”

We werken ons op vanaf de absolute basis. Daarna laten we je een paar snelkoppelingen zien die het schrijven van filters wat sneller en minder uitgebreid maken. Tot slot bekijken we een aantal filtervoorbeelden uit de praktijk.

Als je geen nieuwkomer in Drupal bent, heb je waarschijnlijk al de Views-module gebruikt voor dit soort dingen. In tegenstelling tot de REST-module die met Drupal Core wordt meegeleverd, exporteert JSON:API geen Views-resultaten. Collecties zijn JSON:API’s API-First vervanger voor geëxporteerde “REST-weergaven” in Views.

Filters bouwen


De fundamentele bouwstenen van de filters van JSON:API zijn condities en groepen. Condities stellen dat iets waar is, en groepen laten je die beweringen samenstellen tot logische sets om grotere conditiegroepen te maken. Die sets kunnen worden genest om superfijne queries te maken. Je kunt die geneste sets zien als een boom:

Conventionele representatie:

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

Boomrepresentatie:

   a
  / \
 b & c
    / \
   d | e

In beide representaties:

"d" en "e" zijn leden van "c" in een OF-groep.
"b" en "c" zijn leden van "a" in een EN-groep.

Dus, wat zit er in een conditie?

Even logisch doen 🖖. Onthoud: een conditie vertelt je een WAAR of ONWAAR ding over een resource en een bewering die jij daarover doet, zoals “is deze entiteit door een bepaalde gebruiker aangemaakt?” Wanneer de conditie ONWAAR is voor een resource, wordt die resource niet in de collectie opgenomen.

Een conditie heeft 3 primaire onderdelen: een pad, een operator en een waarde.

  • Een ‘pad’ identificeert een veld op een resource
  • Een ‘operator’ is een vergelijkingsmethode
  • Een ‘waarde’ is het ding waartegen je vergelijkt

In pseudo-code is een conditie iets dat er zo uitziet:

($field !== 'space')

Waarbij:

  1. $field het veld is van de resource die door het ‘pad’ is geïdentificeerd
  2. de ‘operator’ is!==
  3. de ‘waarde’ de string 'space' is

In de JSON:API-module kunnen we het niet zo mooi laten lijken, omdat we het binnen een URL-querystring moeten laten werken. Om dat te doen, representeren we elke conditie met key/value-paren.

Als we zouden filteren op de voornaam van een gebruiker, zou een conditie er ongeveer zo uitzien:

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

Let op dat we een label in de eerste set vierkante haken hebben gezet. We hadden er net zo goed b-label of this_is_my_super_awesome_label of zelfs een geheel getal zoals 666 van kunnen maken 🤘😅. Het punt is dat elke conditie of groep een identificator moet hebben.

Maar wat als we veel Janissen in het systeem hebben?

Laten we een extra filter toevoegen zodat we alleen Janissen krijgen met een achternaam die begint met “J”:

?filter[first-name-filter][condition][path]=field_first_name
&filter[first-name-filter][condition][operator]=%3D  <- gecodeerd "="
&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

Misschien is het meervoud van Janis wel “Janii” 🤔...

Er zijn veel meer filteroperators dan alleen = en STARTS_WITH. Hier is de volledige lijst, rechtstreeks uit de JSON:API-codebase:

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

Symbool-operators moeten URL-gecodeerd worden. Je kunt de juiste codering krijgen met PHP’s urlencode() functie.

Conditiegroepen


Nu weten we hoe we condities bouwen, maar nog niet hoe we groepen van condities bouwen. Hoe bouwen we een boom zoals we die hierboven zagen?

Daarvoor hebben we een “groep” nodig. Een groep is een set condities samengevoegd door een “conjunctie”. Alle groepen hebben conjuncties en een conjunctie is ofwel AND (EN) of OR (OF).

Nu is ons filter een beetje te specifiek! Stel dat we alle gebruikers willen vinden met een achternaam die begint met “J” en óf de voornaam “Janis” of de voornaam “Joan”.

Daarvoor voegen we een groep toe:

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

Vervolgens moeten we onze filters aan die nieuwe groep toewijzen.

Dat doen we door een memberOf key toe te voegen. Elke conditie en groep kan een memberOf key hebben. 

Tip: Groepen kunnen net als condities een memberOf key hebben, wat betekent dat we groepen van groepen kunnen maken 🤯!

Opmerking: Elk filter zonder een memberOf key wordt verondersteld deel uit te maken van een “root”-groep met een conjunctie AND.

Alles bij elkaar:

?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

Komt dat bekend voor?

Dat zou moeten, we zagen het hierboven als een boom:

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

Je kunt deze groepen zo diep nesten als je maar wilt.

Paden

Condities hebben nog één functie: ‘paden’

Paden bieden een manier om op relatie­waarden te filteren.

Tot nu toe hebben we alleen gefilterd op de hypothetische field_first_name en field_last_name op de user-resource.

Stel dat we willen filteren op de naam van iemands carrière, waarbij carrièretakken als een aparte resource zijn opgeslagen. We zouden dan zo’n filter kunnen toevoegen:

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

Paden gebruiken een “dotnotatie” om relaties te doorlopen.

Als een resource een relatie heeft, kun je daartegen filteren door de naam van het relatieveld en de veldnaam van de gerelateerde resource aan elkaar te koppelen met een . (punt).

Je kunt zelfs filteren op relaties van relaties (enzovoort) door meer veldnamen en punten toe te voegen.

Tip: Je kunt op een specifieke index van een relatie filteren door een niet-negatief geheel getal in het pad te zetten. Dus het pad some_relationship.1.some_attribute zou alleen filteren op de 2e gerelateerde resource.

Tip: Je kunt filteren op subeigenschappen van een veld. Bijvoorbeeld, een pad zoals field_phone.country_code werkt ook al is field_phone geen relatie.

Tip: Bij het filteren op configuratie-eigenschappen kun je een asterisk (*) gebruiken als wildcard voor elk deel van een pad. Bijvoorbeeld, /jsonapi/field_config/field_config?filter[dependencies.config.*]=comment.type.comment zou alle field configs matchen waarbij ["attributes"]["dependencies"]["config"] (een geïndexeerde array) de waarde “comment.type.comment” bevat.

Snelkoppelingen


Dat zijn een hoop tekens om te typen. Meestal heb je zulke ingewikkelde filters niet nodig; voor die gevallen heeft de JSON:API-module een paar “snelkoppelingen” om filters sneller te schrijven.

Als de operator = is, hoef je die niet op te nemen. Die wordt gewoon aangenomen. Dus:

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

wordt

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

Het is ook zeldzaam dat je twee keer op hetzelfde veld moet filteren (al kan het). Dus wanneer de operator = is en je niet twee keer op hetzelfde veld hoeft te filteren, kan het pad de identificator zijn. Dus:

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

wordt

?filter[field_first_name][value]=Janis

Die extra value is lastig. Daarom kun je de eenvoudigste gelijkheidscontroles reduceren tot een key-value-vorm:

?filter[field_first_name]=Janis

Filters en toegangscontrole


Eerst een waarschuwing: verwar filters niet met toegangscontrole. Alleen omdat je een filter hebt geschreven om iets te verwijderen dat een gebruiker niet zou mogen zien, betekent niet dat het niet toegankelijk is. Voer altijd toegangscontroles uit aan de backend.

Met die belangrijke kanttekening, laten we het hebben over het gebruiken van filters als aanvulling op toegangscontrole. Om de performance te verbeteren, moet je wegfilteren wat je gebruikers toch niet kunnen zien. De meest voorkomende supportvraag in de JSON:API-issuequeue kan met dit ene simpele trucje worden opgelost!

Als je weet dat je gebruikers ongepubliceerde inhoud niet kunnen zien, voeg dan het volgende filter toe:

?filter[status][value]=1

Met deze methode verlaag je het aantal onnodige verzoeken dat je moet doen. Dat komt omdat JSON:API geen data teruggeeft voor resources waarvoor een gebruiker geen toegang heeft. Je kunt zien welke resources mogelijk zijn beïnvloed door de meta.errors sectie van het JSON:API-document te inspecteren.

Dus, doe je best om ontoegankelijke resources vooraf weg te filteren.

Filtervoorbeelden


1. Alleen gepubliceerde nodes ophalen

Een zeer veelvoorkomend scenario is om alleen de nodes te laden die gepubliceerd zijn. Dit is een heel eenvoudig filter.

KORT
filter[status][value]=1

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

2. Nodes ophalen op basis van de waarde van een entiteitsreferentie

Een veelgebruikte strategie is om inhoud te filteren via een entiteitsreferentie.

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

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

Om volledig te voldoen aan de JSON API-specificatie: hoewel Drupal intern de uuid eigenschap gebruikt, gebruikt JSON API id in plaats daarvan.

Sinds Drupal 9.3 is het ook mogelijk om te filteren op target_id in plaats van alleen te filteren op de uuid eigenschap.

KORT
filter[field_tags.meta.drupal_internal__target_id]=1

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

3. Geneste filters: Nodes ophalen die zijn aangemaakt door gebruiker admin

Het is mogelijk om te filteren op velden van gerelateerde entiteiten zoals de gebruiker, taxonomievelden of elk entiteitsreferentieveld. Dit doe je eenvoudig met de volgende notatie: reference_field.nested_field. In dit voorbeeld is het referentieveld uid voor de gebruiker en name is een veld van de user-entiteit.

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

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

4. Filteren met arrays: Nodes ophalen die zijn aangemaakt door gebruikers [admin, john].

Je kunt meerdere waarden aan een filter meegeven om in te zoeken. Naast de keys field en value kun je een operator aan je conditie toevoegen. Meestal is het “=”, maar je kunt ook “IN”, “NOT IN”, “>”, “<”, “<>”, “BETWEEN” gebruiken.

Voor dit voorbeeld gebruiken we de IN-operator. Let op dat ik twee vierkante haken achter value heb toegevoegd om er een array van te maken.

NORMAAL
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

Tip: wanneer je vierkante haken gebruikt voor filters met meerdere waarden, gebruik dan niet gewoon lege vierkante haken voor een nieuwe waarde.
Hoewel deze werken wanneer ze in de URL worden getypt, zullen Guzzle en andere HTTP-clients slechts één waarde maken omdat de arraykey hetzelfde wordt gezien en de vorige waarde wordt overschreven. Het is beter om een index te gebruiken om unieke array-elementen te maken.

5. Groeperen van filters: Nodes ophalen die gepubliceerd zijn en aangemaakt door admin.

Laten we nu enkele voorbeelden hierboven combineren en het volgende scenario maken.
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

Je hoeft de and-group niet echt toe te voegen, maar ik vind dat meestal wat duidelijker.

6. Groepen groeperen: Nodes ophalen die gepromoot of sticky zijn en aangemaakt door admin

Zoals vermeld in de sectie over groeperen, kun je groepen in andere groepen plaatsen.
WHERE (user.name = admin) AND (node.sticky = 1 OR node.promoted = 1)

Om dit te doen plaatsen we sticky en promoted in een groep met conjunctie OR. Maak een groep met conjunctie AND en plaats
de admin-filter en de promoted/sticky-OF-groep daarin.

# Maak een AND- en een OR-GROEP
filter[and-group][group][conjunction]=AND
filter[or-group][group][conjunction]=OR

# Plaats de OR-groep in de AND-GROEP
filter[or-group][group][memberOf]=and-group

# Maak de admin-filter en plaats die in de AND-GROEP
filter[admin-filter][condition][path]=uid.name
filter[admin-filter][condition][value]=admin
filter[admin-filter][condition][memberOf]=and-group

# Maak de sticky-filter en plaats die in de OR-GROEP
filter[sticky-filter][condition][path]=sticky
filter[sticky-filter][condition][value]=1
filter[sticky-filter][condition][memberOf]=or-group

# Maak de promoted-filter en plaats die in de OR-GROEP
filter[promote-filter][condition][path]=promote
filter[promote-filter][condition][value]=1
filter[promote-filter][condition][memberOf]=or-group

7. Filter voor nodes waar ‘title’ “Foo” bevat

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

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

8. Filteren op niet-standaard complexe velden (bijv. addressfield)

FILTER OP LOCALITEIT
filter[field_address][condition][path]=field_address.locality
filter[field_address][condition][value]=Mordor

FILTER OP ADRESREGEL
filter[address][condition][path]=field_address.address_line1
filter[address][condition][value]=Rings Street

 9. Filteren op waarden van taxonomietermen (bijv. tags)

Voor filtering moet je de machinale naam van de woordenschat (vocabulary) en het veld gebruiken dat op je node aanwezig is.

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

 10. Filteren op datum (alleen datum, geen tijd)

Datums zijn te filteren. Geef een tijdstring door die voldoet aan het ISO-8601-formaat.

Dit voorbeeld is voor een Datum-veld dat is ingesteld op alleen datum (geen tijd).

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

Dit voorbeeld is voor een Datum-veld dat datum en tijd ondersteunt.

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

Let op dat timestamp-velden (zoals created of changed) momenteel een timestamp moeten gebruiken voor filtering:

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

 11. Filteren op lege array-velden

Dit voorbeeld is voor een veld met Keuzevakjes/Radioknoppen zonder geselecteerde waarde. Stel, je hebt een veld dat een checkbox is. Je wilt alle nodes ophalen waarbij die waarde niet is aangevinkt. Wanneer aangevinkt, retourneert JSON API een array:

"my_field":["checked"] 

Wanneer niet aangevinkt, retourneert JSON API een lege array:

"my_field": [] 

Als je alle velden wilt ophalen die niet aangevinkt zijn, moet je IS NULL gebruiken op de array als volgt (zonder waarde):

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

Artikel uit de Drupal-documentatie.