You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/5.x/system/relations.md
+74-20Lines changed: 74 additions & 20 deletions
Original file line number
Diff line number
Diff line change
@@ -197,23 +197,46 @@ Passing an array returns results related to _any_ of the supplied elements. This
197
197
.section('recipes')
198
198
.relatedTo(proteins)
199
199
.all() %}
200
-
{# -> Recipes that share one or more proteins with the current one. #}
200
+
{# -> Recipes that share *any* proteins with the current one. #}
201
201
```
202
202
203
-
Passing `and` at the beginning of an array returns results relating to *all* of the supplied items:
203
+
Passing `and` at the beginning of an array returns results related to *all* of the supplied items:
204
204
205
205
```twig
206
206
{% set proteins = entry.ingredients.foodGroup('protein').all() %}
207
207
208
208
{% set moreRecipes = craft.entries()
209
209
.section('recipes')
210
-
.relatedTo(['and'] | merge(proteins))
210
+
.relatedTo(['and', ...proteins]))
211
211
.all() %}
212
-
{# -> Recipes that also use all this recipe’s proteins. #}
212
+
{# -> Recipes that also use *all* this recipe’s proteins. #}
213
213
```
214
214
215
215
This is equivalent to `.relatedTo(['and', beef, pork])`, if you already had variables for `beef` and `pork`.
216
216
217
+
### Empty and Not Empty
218
+
219
+
Like most other fields, relational field query methods accept special `:notempty:` and `:empty:` tokens.
220
+
This allow you to query for elements that are related (or _not_ related) to _any_ other element—useful when the existence of a relationship has meaning:
221
+
222
+
```twig
223
+
{% set smallKitchenRecipes = craft.entries()
224
+
.section('recipes')
225
+
.specialEquipment(':empty:')
226
+
.all() %}
227
+
{# -> Recipes that don’t require any specialized cookware. #}
228
+
```
229
+
230
+
You can still use the `specialEquipment` field’s data like any other relational field:
231
+
232
+
```twig
233
+
{% set equipment = entry.specialEquipment.all() %}
234
+
235
+
{% if equipment %}
236
+
{# Output a list of cookware... #}
237
+
{% endif %}
238
+
```
239
+
217
240
## Compound Criteria
218
241
219
242
Let’s look at how we might combine multiple relational criteria:
@@ -260,14 +283,14 @@ Property
260
283
: One of `element`, `sourceElement`, or `targetElement`
261
284
262
285
Accepts
263
-
: Element ID, element, [element query](../development/element-queries.md), special `:empty:` or `:notempty:` tokens, or an array thereof
286
+
: Element ID, element, [element query](../development/element-queries.md), or an array thereof
264
287
265
288
Description
266
-
: - Use `element` to get results on either end of a relational field (source _or_ target);
267
-
: - Use `sourceElement` to return elements selected in a relational field on the provided element(s);
268
-
: - Use `targetElement` to return elements that have the provided element(s) selected in a relational field.
289
+
: - Use `element` to allow results on either end of a relational field (source _or_ target);
290
+
: - Use `sourceElement` to scope the query to elements selected in a relational field on the provided element(s);
291
+
: - Use `targetElement` to scope the query to elements that have the provided element(s) selected in a relational field.
269
292
270
-
One way of thinking about the difference between `sourceElement` and `targetElement` is that specifying a _source_is roughly equivalent to using a field handle:
293
+
One way of thinking about the difference between `sourceElement` and `targetElement` is that specifying a _single source_is roughly equivalent to accessing related elements [via a field handle](#custom-fields):
271
294
272
295
```twig
273
296
{% set ingredients = craft.entries()
@@ -276,10 +299,21 @@ One way of thinking about the difference between `sourceElement` and `targetElem
276
299
sourceElement: recipe,
277
300
})
278
301
.all() %}
279
-
{# -> Equivalent to `recipe.ingredients.all()`, from our very first example! #}
302
+
{# -> Similar to `recipe.ingredients.all()`, from our very first example! #}
280
303
```
281
304
282
-
The special `:notempty:` and `:empty:` tokens allow you to query for elements that are related (or _not_ related) to _any_ other element. These can be combined with [field](#fields) and [site](#sites) conditions.
305
+
Using an _array of source elements_ returns any elements used as a relation on one or more of those sources—but each of the related elements will only appear in the results once!
306
+
307
+
```twig
308
+
{% set favorites = currentUser.favoriteRecipes.ids() %}
309
+
{% set likedIngredients = craft.entries()
310
+
.section('ingredients')
311
+
.relatedTo({
312
+
sourceElement: favorites,
313
+
})
314
+
.all() %}
315
+
{# -> Any ingredients in recipes you’ve added to your favorites. #}
316
+
```
283
317
284
318
### Fields
285
319
@@ -312,6 +346,8 @@ By being explicit about the field we want the relation to use, we can show the u
312
346
The `field` param does not honor handles overridden in a field layout. Craft doesn’t know until elements are actually loaded which field layouts are relevant.
313
347
:::
314
348
349
+
The `field` param has [additional features](#relations-via-matrix) when scoping a query to a Matrix field.
350
+
315
351
### Sites
316
352
317
353
Property
@@ -357,18 +393,36 @@ Here, we’re allowing Craft to look up substitutions defined in any site, which
357
393
358
394
Relational fields on nested entries within Matrix fields are used the same way they would be on any other element type.
359
395
360
-
If you want to find elements related to a source element through a [Matrix](../reference/field-types/matrix.md) field, pass the Matrix field’s handle to the `field` parameter:
396
+
If you want to find elements related to a source element through a [Matrix](../reference/field-types/matrix.md) field, pass the Matrix field’s handle to the [`field` parameter](#fields):
361
397
362
398
```twig{5}
363
399
{% set relatedRecipes = craft.entries()
364
-
.section('recipes')
365
-
.relatedTo({
366
-
targetElement: ingredient,
367
-
field: 'ingredients'
368
-
})
369
-
.all() %}
400
+
.section('recipes')
401
+
.relatedTo({
402
+
targetElement: ingredient,
403
+
field: 'steps'
404
+
})
405
+
.all() %}
370
406
```
371
407
372
-
In this example, we’ve changed our schema a bit: ingredients are now attached to nested entries in a `steps` Matrix field, so we can tie instructions and quantities or volume to each one. We still have access to all the same relational query capabilities!
408
+
In this example, we’ve changed our schema a bit: ingredients are now attached to nested entries in a `steps` Matrix field.
409
+
We still have access to all the same relational query capabilities!
410
+
411
+
We’ve specified a field handle to ensure that only elements selected as relations via a specific field are returned.
412
+
If you need to be more precise about the field those relationships come from, you can use a dot (`.`) to target a nested field:
413
+
414
+
```twig{5}
415
+
{% set relatedRecipes = craft.entries()
416
+
.section('recipes')
417
+
.relatedTo({
418
+
targetElement: ingredient,
419
+
field: 'steps.ingredients',
420
+
})
421
+
.all() %}
422
+
```
373
423
374
-
We’ve also specified a field handle, to ensure that the relationships are defined through the intended field; if nested entries have multiple relational fields, it’s possible to get “false positive” results!
424
+
::: warning
425
+
Despite similarities to [eager-loading notation](../development/eager-loading.md#matrix-and-field-contexts), the `field` param does does _not_ support narrowing by entry type or beyond the first level of nested elements.
426
+
Relations in any fields with handles matching the second segment (after the dot) are considered.
427
+
Local handles are honored here, because Craft knows which entry types (and therefore field layouts) may be used by the nested entries.
0 commit comments