Backend
Relation manager
Edit belongsTo and belongsToMany relations in admin forms and manage linked records with config_relation.yaml, bound to models the controller names.
On this page
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 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:
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):
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. 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. A controller that maps a relation column to a physical column itself implements pact.ListRelationColumnMapper.