57 lines
3.6 KiB
Markdown
57 lines
3.6 KiB
Markdown
---
|
|
title: Relation manager
|
|
description: Edit belongsTo and belongsToMany relations in admin forms and manage linked records with config_relation.yaml, bound to models the controller names.
|
|
section: backend
|
|
order: 40
|
|
---
|
|
# Relation manager
|
|
|
|
WinterCMS edits relations in two ways: a `relation` form field that picks the related record, and the Relation behaviour, which embeds a list of linked records with link and unlink buttons. [cabana](../../modules/cabana/README.md) has both. The one rule that differs from WinterCMS: the framework never guesses a table, pivot or foreign key name. The controller supplies every name, and a missing or wrong one stops the start-up.
|
|
|
|
## Relation fields
|
|
|
|
A `type: relation` field in `fields.yaml` picks a belongsTo record or a set of belongsToMany records. `nameFrom` names the related model's label column:
|
|
|
|
```yaml
|
|
category:
|
|
label: acme.blog::lang.posts.category
|
|
type: relation
|
|
nameFrom: name
|
|
```
|
|
|
|
The controller implements `cabana.FieldRelationProvider` and returns a `cabana.FieldRelationContract` per field: its `Kind` (`belongsTo` or `belongsToMany`), a factory for the related model, and the `ForeignKey` of a belongsTo or the pivot model and its two key columns for a belongsToMany. An optional `OrderColumn` on the pivot stores the order in which the administrator picked the records.
|
|
|
|
The SPA loads the choices, paginated, from `.../fields/{field}/options`, and every record response carries the display labels of the linked records. A controller that implements `pact.RelationExtendOptionsQuery` narrows the choices, and the same scoped query rechecks the submitted IDs on save, so a record it does not offer cannot be attached.
|
|
|
|
## Relation managers
|
|
|
|
A `type: relation-manager` field embeds a relation manager in the form. Its `relation` key names an entry in the controller's `config_relation.yaml`, which describes the two panels: the linked records (`view`) and the candidates shown when linking (`manage`):
|
|
|
|
```yaml
|
|
editors:
|
|
label: acme.blog::lang.posts.editors
|
|
view:
|
|
list:
|
|
columns:
|
|
name:
|
|
label: acme.blog::lang.editors.name
|
|
email:
|
|
label: acme.blog::lang.editors.email
|
|
toolbarButtons: link|unlink
|
|
showSearch: true
|
|
manage:
|
|
list:
|
|
columns:
|
|
name:
|
|
label: acme.blog::lang.editors.name
|
|
showSearch: true
|
|
```
|
|
|
|
The controller implements `cabana.AdminRelationContractProvider` and returns a `cabana.RelationContract` per relation: the related and pivot model factories, the pivot's two foreign keys, a map from column names in the YAML to physical columns, and optionally the pivot columns a hook may set and a function that excludes candidate IDs, such as the parent itself. A relation in the YAML without a contract, a contract without a relation, or a relation without a `relation-manager` field stops the start-up.
|
|
|
|
`cabana.RelationService` serves the panels: linked records, link candidates, link and unlink, under `.../{id}/relations/{name}`. Link and unlink run in a transaction. The `view` panel's `toolbarButtons` decide which of the two the server accepts: a relation that does not list `unlink` answers 403 `forbidden` on the unlink route, and likewise for `link`. `pact.RelationExtendManageQuery` scopes the candidates, and `pact.RelationBeforeLink` can check or fill pivot columns before a link is written.
|
|
|
|
## Relations in lists
|
|
|
|
A list column can show a related value with `relation` and `select` in `columns.yaml`; see [Lists and filters](lists-and-filters.md). A controller that maps a relation column to a physical column itself implements `pact.ListRelationColumnMapper`.
|