Skip to content

Commit

Permalink
DOC Document multi-reciprocal has_one relations (#417)
Browse files Browse the repository at this point in the history
  • Loading branch information
GuySartorelli authored Dec 11, 2023
1 parent dd3ffe9 commit fd9af10
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
66 changes: 66 additions & 0 deletions en/02_Developer_Guides/00_Model/02_Relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,70 @@ be necessary. For example additional parent classes for each respective relation
duplication of code.
[/warning]

### Multi-relational `has_one` {#multi-relational-has-one}

A single `has_one` relation can be allowed to manage multiple `has_many` relations - this is especially useful
in situations like adding multiple lists of links to a [`SiteConfig`](api:SilverStripe\SiteConfig\SiteConfig) to build a menu.

An additional column is created called `<relationship-name>Relation`, along with the `<relationship-name>Class`
and `<relationship-name>ID` columns of a normal polymorphic `has_one` relation.

[warning]
If you save records into the `has_one` relation programatically, you must set the relation in the
`<relationship-name>Relation` field, or it won't be included when you fetch the `has_many` relation list.

Generally it is better to instead add the record with the `has_one` relation into its corresponding `has_many` relation
directly - see [adding relations](#adding-relations).
[/warning]

To specify that a `has_one` relation is multi-relational define the relation like so:

```php
namespace App\Model;

use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;

class Fan extends DataObject
{
// ...

private static array $has_one = [
'FanOf' => [
// The class here is the class for the has_one - it must be polymorphic.
'class' => DataObject::class,
// Setting this to true is what defines this has_one relation as multi-relational
DataObjectSchema::HAS_ONE_MULTI_RELATIONAL => true,
],
];
}
```

[hint]
Multi-relational `has_one` relations *must* be [polymorphic](#polymorphic-has-one).
[/hint]

It is best practice for your `has_many` relations to indicate which relation they're pointing at using dot notation. For example:

```php
namespace App\Model;

use SilverStripe\ORM\DataObject;

class Team extends DataObject
{
// ...

private static array $has_many = [
// Notice that these are both pointing at the same has_one relation!
'CheapFans' => Fan::class . '.FanOf',
'VipFans' => Fan::class . '.FanOf',
];
}
```

See [`has_many`](#has_many) below for more details about `has_many` relations.

### `belongs_to`

Defines a one-to-one relationship with another object, which declares the other end of the relationship with a
Expand Down Expand Up @@ -215,6 +279,8 @@ SilverStripe\Assets\Image:
Team: App\Model\Team
```
You can point multiple `has_many` relations at a single `has_one` relation if you use a [multi-relational `has_one`](#multi-relational-has-one).

Note that in some cases you may be better off using a `many_many` relation instead. Carefully consider whether you are defining a "one-to-many" or a "many-to-many" relationship.
[/alert]

Expand Down
41 changes: 41 additions & 0 deletions en/04_Changelogs/5.2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,47 @@ title: 5.2.0 (unreleased)

This release comes jampacked with new ORM features, granting you access to some new abstractions for more powerful and efficient queries.

#### Multi-relational `has_one` relations

Traditionally, if you wanted to have multiple `has_many` relations for the same class, you would have to include a separate `has_one` relation for *each* `has_many` relation.

This release includes a new `has_one` syntax to declare that your `has_one` should be allowed to handle multiple reciprocal `has_many` relations. The syntax for that is as follows:

[hint]
Multi-relational `has_one` relations *must* be polymorphic.
[/hint]

```php
namespace App\Model;

use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;

class MyExample extends DataObject
{
// ...

private static array $has_one = [
'MyMultiRelationalRelation' => [
// The class here is the class for the has_one - it must be polymorphic.
'class' => DataObject::class,
// Setting this to true is what defines this has_one relation as multi-relational
DataObjectSchema::HAS_ONE_MULTI_RELATIONAL => true,
],
];
}
```

Multiple `has_many` relations on a single class can point to the above `has_one` relation, and they will be correctly saved and resolved when you get the relation list.

[warning]
This new feature means sometimes the value in the associative `has_one` configuration array will be an array, rather than a class name.
If you are relying on fetching this configuration to find the class names of `has_one` relations, consider using
[`DataObject::hasOne()`](api:SilverStripe\ORM\DataObject::hasOne()) or [`DataObjectSchema::hasOneComponent()`](api:SilverStripe\ORM\DataObjectSchema::hasOneComponent()) instead.
[/warning]

See [multi-relational `has_one` in the relations docs](/developer_guides/model/relations/#multi-relational-has-one) for more details about this relation type.

#### UNION clause {#orm-union-clause}

Abstractions for the SQL `UNION` clause have been added to `SQLSelect` and `DataQuery`.
Expand Down

0 comments on commit fd9af10

Please sign in to comment.