CodamAIDocs
Themafertig

Many-to-Many über eine Verbindungstabelle

Wie n:m-Beziehungen über ein eigenes Verbindungsmodell abgebildet werden und was das beim Schreiben bedeutet.

Ausprägungen
MANYTOMANY mit generierter Verbindungstabelleeigenes Verbindungsmodell mit zwei n:1Verbindung anlegenVerbindung entfernenein Ende löschen

Worum es geht

Bei einer n:m-Beziehung (Many-to-Many) stehen auf beiden Seiten viele Objekte: Eine Gruppe hat viele Rollen, eine Rolle steckt in vielen Gruppen. Eine relationale Datenbank bildet das mit einer dritten Tabelle ab, der Verbindungstabelle: je Verbindung eine Zeile mit den beiden ids.

In CDMS gibt es dafür zwei Wege:

Zwei Wege zu n:m
MANYTOMANYVerbindungsmodell
modelliert alsein Feldpaar vom Typ MANYTOMANYein eigenes Modell mit zwei MANYTOONE-Feldern
Verbindungstabelleerzeugt der Generator, du siehst sie nichtist ein normales Modell mit API und Rollen
eigene Felder an der Verbindungneinja, z. B. gültig von/bis
im JSON"roles": [{ "id": … }]"groups": [{ "group": { "id": … } }]
gut fürreine Zuordnung, z. B. Gruppen und RollenZuordnungen mit Eigenschaften, z. B. Mitgliedschaft mit Zeitraum

Weg 1: MANYTOMANY

erDiagram
    ROLE ||--o{ SYSTEM_ROLE_GROUPS : "groups"
    GROUP ||--o{ SYSTEM_ROLE_GROUPS : "roles"
    ROLE {
        uuid id
        string name
    }
    GROUP {
        uuid id
        string name
    }
    SYSTEM_ROLE_GROUPS {
        uuid role_id
        uuid group_id
    }

Du legst im Hub an einem der beiden Modelle ein Feld vom Typ MANYTOMANY an, die Gegenseite entsteht mit demselben Typ. Die Verbindungstabelle (hier system-role-groups) erzeugt der Generator. Sie hat nur die beiden Fremdschlüssel.

Gruppe mit zwei neuen Rollen anlegen
Anfrage
POST /api/rest/system/group/create
{
  "data": {
    "name": "Vertrieb",
    "roles": [ { "name": "angebot-lesen" }, { "name": "angebot-schreiben" } ]
  },
  "response": ["id", "name", { "field": "roles", "response": ["id", "name"] }]
}
Antwort
{
  "data": {
    "id": "g1…",
    "name": "Vertrieb",
    "roles": [
      { "id": "r1…", "name": "angebot-lesen" },
      { "id": "r2…", "name": "angebot-schreiben" }
    ]
  },
  "meta": { "error": false }
}

Das geht, weil roles an der Gruppe das Flag CREATE hat. Eine bestehende Rolle verknüpfst du mit ihrer id: "roles": [{ "id": "r1…" }]. Von der anderen Seite geht es genauso: "groups": [{ "id": "g1…" }] an einer Rolle.

Weg 2: ein Verbindungsmodell

erDiagram
    USER ||--o{ GROUP2USER : "groups"
    GROUP ||--o{ GROUP2USER : "users"
    USER {
        uuid id
        string lastname
    }
    GROUP2USER {
        uuid id
        datetime rangeFrom
        datetime rangeTo
    }
    GROUP {
        uuid id
        string name
    }

Hier ist die Verbindung ein eigenes Modell Group2User mit zwei Pflichtfeldern vom Typ MANYTOONE (user, group) und eigenen Feldern (rangeFrom, rangeTo). User.groups und Group.users sind 1:n-Listen auf das Verbindungsmodell, jeweils mit CREATE, UPDATE und DELETE.

- name: "Group2User"
  fields:
    - name: "user"
      relationship: "MANYTOONE"
      reference: { id: "m-user", name: "groups" }
      rules: { required: true }
    - name: "group"
      relationship: "MANYTOONE"
      reference: { id: "m-group", name: "users" }
      rules: { required: true }
    - name: "rangeFrom"
      "@type": "DateTimeField"
    - name: "rangeTo"
      "@type": "DateTimeField"
Benutzer einer Gruppe zuordnen
Anfrage
PATCH /api/rest/user/update/u1…
{
  "data": {
    "id": "u1…",
    "groups": [
      { "group": { "id": "e882…" }, "rangeFrom": "2026-10-01 00:00:00" }
    ]
  },
  "response": ["+", { "field": "groups", "response": ["+",
                { "field": "group", "response": ["name"] }] }]
}
Antwort
{
  "data": {
    "id": "u1…",
    "lastname": "Müller",
    "groups": [
      { "id": "m1…", "rangeFrom": "2026-10-01 00:00:00", "rangeTo": null,
        "group": { "id": "e882…", "name": "Vertrieb" } }
    ]
  },
  "meta": { "error": false }
}

Jeder Eintrag in groups ist eine Verbindungszeile. Ohne id legt CDMS sie an. Das Pflichtfeld user der Zeile schickst du nicht mit, CDMS setzt es selbst, weil die Zeile über den Benutzer angelegt wird. group ist ein reiner Verweis per id.

Verbindung entfernen, ein Ende löschen

Was beim Entfernen passiert

Wann: Du lässt eine Rolle aus roles weg, die Beziehung hat kein DELETE-Flag.

  1. 1
    CDMS→Database
    löscht die Zeile in der Verbindungstabelle
  2. 2
    CDMS
    die Rolle selbst bleibt, auch ihre Verbindungen zu anderen Gruppen

Ergebnis: Nur die Verbindung ist weg. Das ist der übliche Fall.

Wann: Du lässt eine Rolle aus roles weg, die Beziehung hat das DELETE-Flag.

  1. 1
    CDMS→Database
    löscht die Zeile in der Verbindungstabelle, genau wie ohne Flag
  2. 2
    CDMS
    die Rolle selbst bleibt, auch ihre Verbindungen zu anderen Gruppen

Ergebnis: An n:m wirkt DELETE nicht: Eine Rolle kann zu weiteren Gruppen gehören, deshalb löst CDMS nur die Verbindung. Auch beim Löschen der Gruppe bleiben ihre Rollen bestehen. Soll eine Rolle wirklich weg, löschst du sie über ihren eigenen Endpunkt.

Wann: Du lässt eine Verbindungszeile aus groups weg, User.groups hat das DELETE-Flag.

  1. 1
    CDMS→Database
    löscht die Verbindungszeile Group2User
  2. 2
    CDMS
    die Gruppe bleibt, weil Group2User.group kein DELETE-Flag hat

Ergebnis: Wird der Benutzer gelöscht, löscht CDMS auch seine Verbindungszeilen. Die Gruppen bleiben.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-generator – EntityProcessor (@ManyToMany, @JoinTable, Tabellenname), CdmsYamlLoader
  • CDMS/cdms-system-layer – AbstractLayer.setReference, removeBackReference, detachOrDeleteMember, recursiveDelete
  • CDMS/cdms-integrationtest – structure/models.yaml (system Role/Group; User/Group2User/Group; Dossier/Dossier2Asset/FileAsset), AbstractManyToManyTest, AbstractUpdateTest (updateAddGroupsToUserWithoutGroups, updateOneToManyWithReference)
  • documentation/30-daten-und-persistenz/03-beziehungen.md
Suchen