Files
summercms/docs/backend/relation-manager.md
Jakub Zych 44bd1446f5 feat(11.1-04): add the Backend section, the remaining Services pages and the concept map links
- docs/backend: admin controllers, forms, lists and filters, relation
  manager, users and permissions, settings, partials and widgets, admin SPA
- docs/services: storage, outbound HTTP, realtime, Web Push, search, parity
  testing and the Frontend and AJAX (not provided) page
- Examples for cabana (with testdata/docs YAML), fetchguard, lighthouse and
  its centrifugo driver, flare, beachcomber and typesense, tide; lighthouse
  and beachcomber TestDocs* regions run on their Postgres harnesses
- concept map rows link their guide pages and the not-provided rows the
  Frontend and AJAX page; index lists Backend, Database and Services
- TestDocsRequiredPages asserts the D-08 section order
2026-09-30 23:18:35 +02:00

3.5 KiB

title, description, section, order
title description section order
Relation manager Edit belongsTo and belongsToMany relations in admin forms and manage linked records with config_relation.yaml, bound to models the controller names. backend 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 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.