Skip to content

Commit 2f14714

Browse files
committed
Correction to source/target params, improve relations via Matrix
1 parent 90f3f22 commit 2f14714

File tree

1 file changed

+74
-20
lines changed

1 file changed

+74
-20
lines changed

docs/5.x/system/relations.md

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -197,23 +197,46 @@ Passing an array returns results related to _any_ of the supplied elements. This
197197
.section('recipes')
198198
.relatedTo(proteins)
199199
.all() %}
200-
{# -> Recipes that share one or more proteins with the current one. #}
200+
{# -> Recipes that share *any* proteins with the current one. #}
201201
```
202202

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

205205
```twig
206206
{% set proteins = entry.ingredients.foodGroup('protein').all() %}
207207
208208
{% set moreRecipes = craft.entries()
209209
.section('recipes')
210-
.relatedTo(['and'] | merge(proteins))
210+
.relatedTo(['and', ...proteins]))
211211
.all() %}
212-
{# -> Recipes that also use all this recipe’s proteins. #}
212+
{# -> Recipes that also use *all* this recipe’s proteins. #}
213213
```
214214

215215
This is equivalent to `.relatedTo(['and', beef, pork])`, if you already had variables for `beef` and `pork`.
216216

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+
217240
## Compound Criteria
218241

219242
Let’s look at how we might combine multiple relational criteria:
@@ -260,14 +283,14 @@ Property
260283
: One of `element`, `sourceElement`, or `targetElement`
261284

262285
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
264287

265288
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.
269292

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):
271294

272295
```twig
273296
{% set ingredients = craft.entries()
@@ -276,10 +299,21 @@ One way of thinking about the difference between `sourceElement` and `targetElem
276299
sourceElement: recipe,
277300
})
278301
.all() %}
279-
{# -> Equivalent to `recipe.ingredients.all()`, from our very first example! #}
302+
{# -> Similar to `recipe.ingredients.all()`, from our very first example! #}
280303
```
281304

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+
```
283317

284318
### Fields
285319

@@ -312,6 +346,8 @@ By being explicit about the field we want the relation to use, we can show the u
312346
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.
313347
:::
314348

349+
The `field` param has [additional features](#relations-via-matrix) when scoping a query to a Matrix field.
350+
315351
### Sites
316352

317353
Property
@@ -357,18 +393,36 @@ Here, we’re allowing Craft to look up substitutions defined in any site, which
357393

358394
Relational fields on nested entries within Matrix fields are used the same way they would be on any other element type.
359395

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):
361397

362398
```twig{5}
363399
{% 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() %}
370406
```
371407

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+
```
373423

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.
428+
:::

0 commit comments

Comments
 (0)