Zum Inhalt springen

Beziehungen

Beziehungen definieren, wie Sammlungen auf Datenbankebene miteinander verbunden sind. Sie ermöglichen Rebase, Folgendes zu tun:

  • Beziehungsauswahlfelder in Entitätsformularen rendern
  • Verwandte Entitäten beim Anzeigen von Vorschauen auflösen
  • Fremdschlüsselbeschränkungen im Drizzle-Schema generieren
  • Kaskadierendes Löschen/Aktualisieren unterstützen

Beziehungen können entweder inline innerhalb der Eigenschaft oder explizit im relations-Array einer Sammlung definiert werden:

Sie können die Beziehung direkt in der Eigenschaft definieren. Das Framework extrahiert diese zur Normalisierungszeit automatisch in das relations[] der Sammlung, sodass Sie keinen separaten relations[]-Eintrag mehr für Eigenschaften benötigen.

const postsCollection: EntityCollection = {
slug: "posts",
name: "Posts",
table: "posts",
properties: {
title: { type: "string", name: "Title" },
content: { type: "string", name: "Content", multiline: true },
author: {
type: "relation",
name: "Author",
target: () => usersCollection,
cardinality: "one",
direction: "owning",
localKey: "author_id"
}
}
};

Für fortgeschrittene Anwendungsfälle oder wenn eine Beziehung nicht direkt einem Formularfeld zugeordnet werden kann, können Sie sie im relations-Array definieren:

const postsCollection: EntityCollection = {
slug: "posts",
name: "Posts",
table: "posts",
properties: {
title: { type: "string", name: "Title" },
content: { type: "string", name: "Content", multiline: true },
author: { type: "relation", name: "Author", relationName: "author" }
},
relations: [
{
relationName: "author",
target: () => usersCollection,
cardinality: "one",
localKey: "author_id"
}
]
};

Ein Fremdschlüssel in dieser Tabelle verweist auf den Primärschlüssel einer anderen Tabelle.

relations: [
{
relationName: "author",
target: () => usersCollection,
cardinality: "one", // This entity has ONE author
direction: "owning", // The FK is on THIS table
localKey: "author_id" // Column on the posts table
}
]

Dies erzeugt: posts.author_id → users.id

Der Fremdschlüssel befindet sich in der Zieltabelle und verweist zurück auf diese Entität.

// On the Users collection:
relations: [
{
relationName: "posts",
target: () => postsCollection,
cardinality: "many", // This user has MANY posts
direction: "inverse", // The FK is on the TARGET table
foreignKeyOnTarget: "author_id" // Column on the posts table
}
]

Zwei Sammlungen, die über eine zwischengeschaltete Verbindungstabelle verbunden sind.

// On the Users collection:
relations: [
{
relationName: "roles",
target: () => rolesCollection,
cardinality: "many",
direction: "owning",
through: {
table: "user_roles", // Junction table name
sourceColumn: "user_id", // FK to this collection
targetColumn: "role_id" // FK to target collection
}
}
]

Dies erzeugt:

CREATE TABLE user_roles (
user_id INTEGER REFERENCES users(id),
role_id INTEGER REFERENCES roles(id),
PRIMARY KEY (user_id, role_id)
);

Um ein Beziehungsfeld in einem Formular zu rendern, fügen Sie eine Eigenschaft mit type: "relation" hinzu:

properties: {
author: {
type: "relation",
name: "Author",
target: () => usersCollection, // Target collection
widget: "select" // "select" (dropdown) or "dialog" (full picker)
}
}

Relationsfeld im Formular

Beim Rendern einer Vorschau (z. B. in einer Tabellenzelle oder einem Referenz-Chip) übernimmt Rebase die Hydration automatisch:

Relationsvorschau in Tabelle

Für komplexe Beziehungen, die mehrere Tabellen durchqueren, verwenden Sie joinPath:

// Users → Permissions through Roles
relations: [
{
relationName: "permissions",
target: () => permissionsCollection,
cardinality: "many",
joinPath: [
{
table: "user_roles",
on: { from: "id", to: "user_id" }
},
{
table: "roles",
on: { from: "role_id", to: "id" }
},
{
table: "role_permissions",
on: { from: "id", to: "role_id" }
},
{
table: "permissions",
on: { from: "permission_id", to: "id" }
}
]
}
]
joinPath: [
{
table: "customers",
on: {
from: ["company_code", "region_id"], // Multiple columns
to: ["code", "region_id"]
}
}
]

Steuern Sie, was passiert, wenn verwandte Entitäten aktualisiert oder gelöscht werden:

relations: [
{
relationName: "author",
target: () => usersCollection,
cardinality: "one",
localKey: "author_id",
onDelete: "cascade", // Delete posts when user is deleted
onUpdate: "cascade" // Update FK when user ID changes
}
]
AktionVerhalten
"cascade"Die Änderung auf verwandte Zeilen übertragen
"restrict"Den Vorgang verhindern, wenn verwandte Zeilen existieren
"no action"Gleich wie restrict (Aufschub bis zur Beschränkungsprüfung)
"set null"Die FK-Spalte auf NULL setzen
"set default"Die FK-Spalte auf ihren Standardwert setzen

Beim Abfragen von Daten über das Rebase Client SDK sind Beziehungen standardmäßig nicht enthalten. Verwenden Sie die Methode include(), um verwandte Entitäten zusammen mit den primären Daten anzufordern.

const { data } = await client.data.articles
.include("author", "categories")
.find();
const { data } = await client.data.articles
.include("*")
.find();
const { data } = await client.data.articles.find({
include: ["author", "categories"]
});

Wenn enthalten, enthält die Antwort sowohl den skalaren Fremdschlüssel als auch das hydrierte Beziehungsobjekt:

const { data } = await client.data.articles
.include("author")
.find();
for (const article of data) {
// Scalar FK — always present
article.values.author_id; // "uuid-1234"
// Hydrated relation — only present when included
article.values.author?.name; // "Jane Doe"
}

Die an include() übergebenen Beziehungsnamen müssen mit dem relationName übereinstimmen, der im relations-Array der Sammlung definiert ist.

Für die vollständige Referenz zum Query Builder (Filtern, Sortieren, Paginierung, Echtzeit) siehe die Client SDK-Dokumentation.

interface Relation {
relationName?: string;
target: () => EntityCollection;
cardinality: "one" | "many";
direction?: "owning" | "inverse";
inverseRelationName?: string;
localKey?: string;
foreignKeyOnTarget?: string;
through?: {
table: string;
sourceColumn: string;
targetColumn: string;
};
joinPath?: JoinStep[];
onUpdate?: "cascade" | "restrict" | "no action" | "set null" | "set default";
onDelete?: "cascade" | "restrict" | "no action" | "set null" | "set default";
overrides?: Partial<EntityCollection>;
validation?: { required?: boolean };
}