profound-logoProfound CMS
⌘K
Admin
Theme
DokumenteTutorialBlogPhilosophie
DokumenteTutorialBlogPhilosophie

Hybrid

Parametrisches RoutingKomponenten-TypenSetup server sent events (SSE) content refetchEinrichtung-Admin-Panel-ProxyScripting im Template-BuilderProject ScaffoldingMedienbibliothek

Headless

SchnellstartSplit Screen JSON Component Builder with LLMComponent Zod Pull

REST-API

REST API OverviewgetWebsite mit der CMS-API verbindengetRouten abrufengetRoute abrufengetBlöcke abrufengetBlöcke mit CEL-Cache abrufengetGET /blocks/generatedgetKomponenten abrufengetKomponentenname abrufengetGET /dataset/{schema_name}getGET /content-changes (SSE)patchPATCH /dataset/{schema_name}postNachbearbeitung der ÜbersetzungpatchPatch-ÜbersetzungengetNutzung abrufenpostPOST /csvpatchPATCH /csv
All Systems Operational
Powered Byprofound-logo
Theme

Scripting im Template-Builder

Ein praktischer Leitfaden zum Schreiben von CEL-Ausdrücken im CMS.

Ein praktischer Leitfaden zum Schreiben von CEL-Ausdrücken im CMS.


So funktioniert CEL

CEL (Common Expression Language) ist eine leichtgewichtige Skriptsprache, die in unser CMS integriert ist. Damit können Sie dynamische Ausdrücke schreiben, die Daten aus Dokumenten abrufen, URL-Parameter lesen und Werte direkt berechnen.

Folgendes geschieht beim Ausführen eines CEL-Skripts:

Ihr Skript                     Die Engine                     Ergebnis
    |                              |                              |
    v                              v                              v
documents.get("article", "intro") --> Ruft Daten aus der Datenbank ab --> { headline: "Welcome", body: "..." }
         .headline                --> Extrahiert das Feld          --> "Welcome"

Stellen Sie sich CEL als schreibgeschützte Abfragesprache vor. Sie kann nichts in der Datenbank ändern – sie liest lediglich Daten und gibt ein berechnetes Ergebnis zurück. Dadurch kann sie überall im CMS sicher verwendet werden.


Die Bausteine

Jeder CEL-Ausdruck kann auf drei Dinge zugreifen:

ObjektWas es istBeispiel
documentsRuft ein beliebiges Dokument aus dem CMS abdocuments.get("country", "us")
metaInformationen zur aktuellen Anfrage (Lokalisierung, URL-Parameter)meta.locale, meta.params.slug
schemaFelddefinitionen des aktuellen Dokumentsschema.fields

Selbstreferenz mit doc

Beim Schreiben von CEL-Ausdrücken innerhalb eines Dokumenteditors können Sie über das Objekt doc auf die Feldwerte des aktuellen Dokuments zugreifen. Dies ermöglicht berechnete Felder und Referenzen zwischen Feldern.

// Auf das Preisfeld des aktuellen Dokuments zugreifen
doc.price

// Gesamtsumme aus Feldern des aktuellen Dokuments berechnen
doc.price * doc.quantity

// Bedingung basierend auf dem Status des aktuellen Dokuments
doc.status == "published" ? doc.title : "Draft: " + doc.title

Das Objekt doc enthält alle Feldwerte des bearbeiteten Dokuments. Dies ist nützlich für:

  • Berechnete Felder (z. B. doc.price * doc.quantity)
  • Bedingte Anzeigelogik basierend auf dem Dokumentstatus
  • Ausdrücke im Stil von Validierungen

Dokumente abrufen

Die leistungsfähigste Funktion von CEL ist das Abrufen von Dokumenten aus beliebigen Bereichen Ihres CMS.

Ein einzelnes Dokument abrufen

Syntax: documents.get(schemaName, identifier)

Angenommen, Sie haben ein article-Dokument mit dem Bezeichner "welcome-post":

// Im CMS gespeichert als: article / welcome-post
{
  "headline": "Welcome to Our Platform",
  "author": "Sarah Chen",
  "body": "We're excited to announce...",
  "tags": ["announcement", "news"]
}

Das gesamte Dokument abrufen:

documents.get("article", "welcome-post")

Gibt zurück:

{
  "headline": "Welcome to Our Platform",
  "author": "Sarah Chen",
  "body": "We're excited to announce...",
  "tags": ["announcement", "news"]
}

Nur die Überschrift abrufen:

documents.get("article", "welcome-post").headline

Gibt zurück: "Welcome to Our Platform"

Den Autor abrufen:

documents.get("article", "welcome-post").author

Gibt zurück: "Sarah Chen"


URL-Parameter verwenden

Wenn Ihre Seite dynamische Routen enthält (z. B. /articles/[slug]), können Sie mit meta.params den URL-Parameter auslesen und das passende Dokument abrufen.

Wenn jemand /articles/welcome-post besucht:

documents.get("article", meta.params.slug).headline

Gibt zurück: "Welcome to Our Platform"

So erstellen Sie dynamische Seiten – dasselbe CEL-Skript funktioniert für jeden Artikel und verwendet einfach den Slug aus der URL.


Mehrere Dokumente abrufen

Syntax: documents.find(schemaName) oder documents.find(schemaName, filter)

// Alle Länder abrufen
documents.find("country")

Gibt zurück:

[
  { "code": "us", "name": "United States", "flag": "US" },
  { "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
  { "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Länder mit einem Filter abrufen
documents.find("country", { "where": { "code": "us" } })

Gibt zurück:

[
  { "code": "us", "name": "United States", "flag": "US" }
]

Übersetzungen

CEL unterstützt das Abrufen übersetzter Dokumentinhalte auf zwei Arten: automatische übersetzungsbasierte Lokalisierung und eine explizite Übersetzungsabfrage.

Automatische Übersetzung über meta.locale

Wenn meta.locale gesetzt ist (z. B. durch Routenparameter oder Benutzereinstellungen), führt documents.get() automatisch übersetzte Inhalte zusammen:

// Wenn meta.locale gleich "fr" ist, wird die französische Übersetzung mit dem Basisdokument zusammengeführt
documents.get("greeting", "welcome").headline

So funktioniert es:

  1. Der Inhalt des Basisdokuments wird abgerufen.
  2. Wenn meta.locale nicht "en" oder "en-US" ist, wird die Übersetzung in der Tabelle translations gesucht.
  3. Übersetzte Felder werden über den Basisinhalt gelegt: { ...baseContent, ...translatedContent }

Das bedeutet: Übersetzte Felder überschreiben die Basisfelder, während nicht übersetzte Felder auf das Basisdokument zurückfallen.

Explizite Übersetzung mit documents.translated()

Wenn Sie unabhängig von der aktuellen Lokalisierung eine bestimmte Übersetzung abrufen müssen:

Syntax: documents.translated(schemaName, identifier, locale)

// Immer die spanische Übersetzung abrufen
documents.translated("greeting", "welcome", "es").headline

// Übersetzung anhand eines URL-Parameters abrufen
documents.translated("product", meta.params.id, meta.params.lang).description

// Übersetzungen vergleichen
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title

Beispiel für Übersetzungen

Ihre Begrüßungsdokumente mit Übersetzungen:

// Basisdokument: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }

// Übersetzung (Sprache: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }

// Übersetzung (Sprache: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }

CEL-Skripte:

// Mit meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Gibt zurück: "Bienvenue"

// Explizite spanische Übersetzung
documents.translated("greeting", "welcome", "es").headline
// Gibt zurück: "Bienvenido"

// Fallback-Muster für fehlende Übersetzungen
documents.translated("greeting", "welcome", meta.params.lang) != null
  ? documents.translated("greeting", "welcome", meta.params.lang).headline
  : documents.get("greeting", "welcome").headline

Praxisbeispiele

Beispiel 1: Titel eines Hero-Blocks aus einem anderen Dokument

Sie haben einen hero-block, der eine Überschrift aus einem article-Dokument anzeigen soll.

Ihr Artikeldokument (Bezeichner: "homepage-hero"):

{
  "headline": "Build Faster, Ship Smarter",
  "subheadline": "The modern CMS for developers"
}

CEL-Skript im Titelfeld des Hero-Blocks:

documents.get("article", "homepage-hero").headline

Ergebnis: Der Hero zeigt "Build Faster, Ship Smarter" an.


Beispiel 2: Ländername anhand eines Codes

Sie erstellen eine Seite unter /countries/[code] und möchten den vollständigen Ländernamen anzeigen.

Ihre Länderdokumente:

// country / us
{ "code": "us", "name": "United States", "flag": "US", "languages": ["en", "es"] }

// country / sa
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA", "languages": ["ar", "en"] }

CEL-Skript:

documents.get("country", meta.params.code).name

Wenn jemand /countries/us besucht:

  • meta.params.code = "us"
  • Ergebnis: "United States"

Wenn jemand /countries/sa besucht:

  • meta.params.code = "sa"
  • Ergebnis: "Saudi Arabia"

Beispiel 3: Bedingte Inhalte basierend auf der Lokalisierung

Zeigen Sie je nach Lokalisierung des Benutzers unterschiedliche Überschriften an.

meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"

Wenn die Lokalisierung "ar-SA" ist: Gibt "Welcome, everyone" zurück. Bei jeder anderen Lokalisierung: Gibt "Welcome" zurück.


Beispiel 4: Verkettete Dokumentabfragen

Ihr article enthält ein Feld countryCode, und Sie möchten den vollständigen Ländernamen abrufen.

Artikeldokument:

{ "headline": "News from the US", "countryCode": "us" }

CEL-Skript:

documents.get("country", documents.get("article", "us-news").countryCode).name

Was geschieht:

  1. documents.get("article", "us-news") gibt { "headline": "News from the US", "countryCode": "us" } zurück.
  2. .countryCode extrahiert "us".
  3. documents.get("country", "us") gibt { "code": "us", "name": "United States", ... } zurück.
  4. .name extrahiert "United States".

Ergebnis: "United States"


Beispiel 5: Fallback-Werte

Wenn ein Dokument möglicherweise nicht existiert, können Sie einen Fallback angeben:

documents.get("article", meta.params.slug) != null
  ? documents.get("article", meta.params.slug).headline
  : "Article Not Found"

Oder prüfen Sie, ob ein bestimmtes Feld existiert:

documents.get("article", "intro").author != null
  ? documents.get("article", "intro").author
  : "Unknown Author"

Beispiel 6: Mit Listen arbeiten

Ihr Artikel enthält Tags, und Sie möchten prüfen, ob ein bestimmter Tag vorhanden ist:

"featured" in documents.get("article", "welcome-post").tags

Gibt zurück: true, wenn der Artikel den Tag "featured" enthält.

Ersten Tag abrufen:

documents.get("article", "welcome-post").tags[0]

Gibt zurück: "announcement" (der erste Tag).

Tags zählen:

size(documents.get("article", "welcome-post").tags)

Gibt zurück: 2 (Anzahl der Tags).


Parametrische Routen und meta.params

Parametrische Routen sind der Schlüssel zum Erstellen dynamischer, lokalisierter Seiten. Wenn Sie ein Routenmuster wie /{lang}/landingPage definieren, extrahiert das CMS die Parameter aus der URL und stellt sie über meta.params bereit.

So funktionieren Routenparameter

Definition des Routenmusters: Routen verwenden die Syntax :paramName oder {paramName}, um dynamische Segmente zu definieren:

MusterBeispiel-URLExtrahierte Parameter
/:lang/landingPage/ko/landingPage{ lang: "ko" }
/{country}/{lang}/products/us/en/products{ country: "us", lang: "en" }
/articles/:slug/articles/welcome-post{ slug: "welcome-post" }

Parameterbindungen: Jeder Routenparameter kann zur Validierung an ein Dokumentschema gebunden werden:

{
  "pattern": "/{lang}/landingPage",
  "param_bindings": {
    "lang": "language"
  }
}

Diese Bindung weist das CMS an:

  1. Das Segment lang aus der URL zu extrahieren.
  2. Es anhand des Schemas language zu validieren (es wird nach einem Dokument gesucht, bei dem content.code übereinstimmt).
  3. Bei erfolgreicher Validierung das vollständige Dokument in den aufgelösten Parametern verfügbar zu machen.

Beispiel: Sprachabhängige Landingpage

Routenkonfiguration:

  • Pfad/Muster: /{lang}/landingPage
  • Muster: /{lang}/landingPage
  • Parameterbindungen: { "lang": "language" }

Ihre Begrüßungsdokumente:

// greeting / ko
{ "code": "ko", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/ko/get-started" }

// greeting / en
{ "code": "en", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/en/get-started" }

// greeting / ja
{ "code": "ja", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Start", "ctaUrl": "/ja/get-started" }

CEL-Skript zum Abrufen lokalisierter Inhalte:

documents.get("greeting", meta.params.lang).headline

Auflösung:

URLmeta.params.langErgebnis
/ko/landingPage"ko""Welcome"
/en/landingPage"en""Welcome"
/ja/landingPage"ja""Welcome"

Fortgeschrittenes Muster: Länder- und Sprachrouten

Für Routen wie /{country}/{lang}/products:

Routenkonfiguration:

{
  "pattern": "/{country}/{lang}/products",
  "param_bindings": {
    "country": "country",
    "lang": "language"
  }
}

CEL-Skripte:

// Ländernamen abrufen
documents.get("country", meta.params.country).name

// Lokalisierte Produktliste anhand des Landes abrufen
documents.find("product", { "where": { "country": meta.params.country } })

// Kombiniert: Länderspezifische Begrüßung in der Sprache des Benutzers anzeigen
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name

Validierungskaskade: Das CMS validiert Parameter hierarchisch. Bei Routen nach dem Muster /{country}/{lang} geschieht Folgendes:

  1. Der Parameter country wird anhand des Schemas country validiert.
  2. Der Parameter lang wird anhand des Schemas language validiert.
  3. Optional wird geprüft, ob lang im Array country.languages[] enthalten ist (hierarchische Validierung).

meta.segments – Zugriff auf den rohen URL-Pfad

meta.segments stellt den rohen URL-Pfad als Array bereit. Dies ist nützlich, wenn Sie ohne benannte Parameter positionsbezogen zugreifen müssen.

So funktioniert es:

URL-Pfadmeta.segments
/articles/tech/ai-news["articles", "tech", "ai-news"]
/ko/landingPage["ko", "landingPage"]
/us/en/products/featured["us", "en", "products", "featured"]
/[]

Wann meta.segments statt meta.params verwenden?

AnwendungsfallBeste Vorgehensweise
Benannte Parameter aus dem Routenmustermeta.params.lang
Positionsbezogener Zugriffmeta.segments[0]
Pfadtiefe ermittelnsize(meta.segments)
Prüfen, ob der Pfad ein Segment enthält"admin" in meta.segments

Beispiele mit meta.segments

// Erstes Segment abrufen (häufig der Sprachcode)
meta.segments[0]

// Pfadtiefe prüfen
size(meta.segments) > 2 ? "deep" : "shallow"

// Prüfen, ob wir uns im Administrationsbereich befinden
"admin" in meta.segments ? "admin mode" : "public mode"

// Fallback: Segment verwenden, wenn der Parameter nicht gebunden ist
has(meta.params.lang) ? meta.params.lang : meta.segments[0]

Vollständige Referenz des meta-Objekts

Das meta-Objekt enthält den gesamten Kontext der aktuellen Anfrage:

EigenschaftTypBeschreibung
meta.localestringAktueller Lokalisierungscode (z. B. "en-US", "ko-KR", "ar-SA")
meta.paramsRecord<string, string>Aus dem URL-Muster extrahierte Routenparameter
meta.segmentsstring[]In Segmente aufgeteilter URL-Pfad
meta.docId`string \null`UUID des aktuellen Dokuments (null bei neuen Dokumenten)
meta.titlestringTitel des aktuellen Dokuments

meta.locale

Der Lokalisierungscode folgt dem BCP-47-Format (Sprache-Region):

// Lokalisierung für von rechts nach links geschriebene Sprachen prüfen
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"

// Nur den Sprachteil abrufen
meta.locale.split("-")[0]  // Nicht unterstützt – verwenden Sie stattdessen meta.params.lang

meta.params

Routenparameter sind immer Zeichenketten. Das CMS validiert sie vor der Auswertung anhand gebundener Schemata:

// Benannten Parameter abrufen
meta.params.lang           // "ko"
meta.params.country        // "us"
meta.params.slug           // "welcome-post"

// Prüfen, ob ein Parameter existiert
has(meta.params.category)  // true/false

// In einem Dokumentabruf verwenden
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)

meta.segments

Rohe URL-Segmente als Array:

// Nach Index zugreifen (nullbasiert)
meta.segments[0]           // Erstes Segment
meta.segments[1]           // Zweites Segment

// Länge prüfen
size(meta.segments)        // Anzahl der Segmente

// Zugehörigkeit prüfen
"products" in meta.segments  // Enthält der Pfad "products"?

meta.docId

Die UUID des aktuellen Dokuments, nützlich für selbstreferenzierende Skripte:

// Nur beim Bearbeiten bestehender Dokumente verfügbar
meta.docId != null ? "editing" : "creating new"

// In bedingter Logik verwenden
meta.docId != null ? documents.get("article", meta.docId).status : "draft"

meta.title

Der Titel des aktuellen Dokuments:

// Zur Anzeige verwenden
"Editing: " + meta.title

// Bedingung basierend auf dem Titel
meta.title.contains("Draft") ? "work in progress" : "published"

documents.ref() – Verkettete Abfragen

Für eine übersichtlichere Syntax, wenn das Schema bekannt ist, der Bezeichner jedoch dynamisch bleibt:

// Herkömmlicher Ansatz
documents.get("airports", meta.params.code).name

// Mit ref() – Schema getrennt vom dynamischen Bezeichner
documents.ref("airports").get(meta.params.code).name

Beide Varianten sind gleichwertig, aber mit ref() wird der dynamische Teil deutlicher.


Kurzübersicht

Dokumente abrufen

documents.get("schema", "identifier")       // Ein Dokument abrufen
documents.get("schema", "id").fieldName     // Ein bestimmtes Feld abrufen
documents.find("schema")                    // Alle Dokumente abrufen
documents.find("schema", { "where": {...}}) // Gefilterte Abfrage
documents.ref("schema").get(identifier)     // Verkettete Abfrage
documents.translated("schema", "id", "fr")  // Mit expliziter Lokalisierung abrufen

Kontextvariablen

meta.locale          // "en-US", "ar-SA" usw.
meta.params.xyz      // URL-Parameter mit dem Namen "xyz"
meta.segments        // URL-Pfad als Array: ["articles", "intro"]
meta.segments[0]     // Erstes Pfadsegment
meta.docId           // ID des aktuellen Dokuments (oder null)
meta.title           // Titel des aktuellen Dokuments
doc.fieldName        // Feldwert des aktuellen Dokuments (im Editor-Kontext)

Operatoren

// Vergleich
==  !=  <  <=  >  >=

// Logik
&&  ||  !

// Ternärer Operator (wenn-dann-sonst)
condition ? valueIfTrue : valueIfFalse

// Zugehörigkeit
"value" in listOrMap

Häufig verwendete Funktionen

size(list)                    // Elemente zählen
size(string)                  // Zeichenkettenlänge
"text".startsWith("te")       // true
"text".endsWith("xt")         // true
"text".contains("ex")         // true
has(object.property)          // Prüfen, ob eine Eigenschaft existiert
hasProperty(obj, "key")       // Prüfen, ob ein Objekt einen Schlüssel besitzt (alternative Syntax)

Fehlermeldungen

Wenn etwas schiefgeht, wird eine der folgenden Meldungen angezeigt:

FehlerBedeutung
SYNTAX_ERRORTippfehler im Skript (fehlendes Anführungszeichen, ungültiger Operator)
TYPE_ERROREs werden inkompatible Datentypen miteinander verwendet
RUNTIME_ERRORDas Skript wurde ausgeführt, ist aber auf ein Problem gestoßen (nicht definierte Variable)
FETCH_LIMIT_EXCEEDEDEs werden zu viele Dokumente abgerufen (maximal 50)
TIMEOUTDas Skript hat zu lange gedauert (maximal 5 Sekunden)
AST_DEPTH_EXCEEDEDDer Ausdruck ist zu tief verschachtelt (maximale Tiefe: 50)
SCRIPT_TOO_LONGDas Skript überschreitet das Limit von 5000 Zeichen

Erweiterbarkeit und zukünftige Funktionen

Die CEL-Engine ist auf Erweiterbarkeit ausgelegt. Zu den geplanten zukünftigen Funktionen gehören:

Geplant: MCP-Server-Integration

// Zukunft: Externe Dienste über MCP aufrufen
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)

Geplant: KI-Funktionen

// Zukunft: KI-gestützte Inhaltsgenerierung
ai.summarize(documents.get("article", meta.params.id).body, 100)
ai.translate(meta.params.text, meta.params.targetLang)
ai.classify(meta.params.input, ["positive", "negative", "neutral"])

Diese Funktionen werden über das registrierte Funktionssystem hinzugefügt und bleiben mit bestehenden Skripten abwärtskompatibel.


Tipps

  1. Autocomplete verwenden – Geben Sie documents. oder meta. ein, damit der Editor verfügbare Optionen anzeigt.
  2. Einfach beginnen – Testen Sie zunächst documents.get("schema", "id") und fügen Sie anschließend .fieldName hinzu.
  3. Auf null prüfen – Wenn ein Dokument möglicherweise nicht existiert, fügen Sie mit != null ? ... : ... einen Fallback hinzu.
  4. Nicht zu viele Daten abrufen – Jeder Aufruf von documents.get() oder documents.find() zählt zum Limit von 50 Abrufen.
  5. meta.params gegenüber meta.segments bevorzugen – Benannte Parameter werden validiert und sind zuverlässiger.
  6. has() für optionale Parameter verwenden – Prüfen Sie has(meta.params.category), bevor Sie auf den Parameter zugreifen.
  7. documents.ref() für dynamische Bezeichner verwenden – Übersichtlichere Syntax, wenn das Schema statisch, der Bezeichner aber dynamisch ist.
  8. doc.fieldName für Selbstreferenzen verwenden – Greifen Sie innerhalb berechneter Ausdrücke auf Felder des aktuellen Dokuments zu.

Referenzen von Dokument zu Dokument

Dieser Abschnitt behandelt fortgeschrittene Muster zum Verknüpfen von Dokumenten und zum Aufbau relationaler Inhaltsstrukturen.

Einfaches Referenzmuster

Die einfachste Form: Ein Dokument verweist anhand eines Bezeichners auf ein anderes.

// Artikel speichert die ID des Autors und ruft dessen Namen ab
documents.get("author", documents.get("article", "intro").authorId).name

Verkettete Abfragen mit documents.ref()

Für eine übersichtlichere Syntax bei dynamischen Bezeichnern:

// Herkömmlicher Ansatz
documents.get("country", documents.get("airport", meta.params.code).countryCode).name

// Mit ref() – übersichtlicher, wenn das Schema bekannt, der Bezeichner aber dynamisch ist
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name

Mehrstufige Referenzketten

Erstellen Sie durch mehrere verkettete Abfragen tiefe Beziehungen:

// Flughafen → Land → Region → Kontinent
documents.get("continent",
  documents.get("region",
    documents.get("country",
      documents.get("airport", meta.params.code).countryCode
    ).regionCode
  ).continentCode
).name

Referenzen mit Übersetzung

Kombinieren Sie Dokumentreferenzen mit Übersetzungen:

// Lokalisierter Ländername für einen Flughafen
documents.translated("country",
  documents.get("airport", meta.params.code).countryCode,
  meta.params.lang
).name

Referenzmuster nach Anwendungsfall

Muster 1: Fremdschlüsselabfrage

Das Dokument speichert eine ID, die auf ein anderes Dokument verweist.

// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// Namen des Autors auflösen
documents.get("author", documents.get("article", meta.params.slug).authorId).name

// Kategorie mit Fallback auflösen
documents.get("article", meta.params.slug).categoryId != null
  ? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
  : "Uncategorized"

Muster 2: Codebasierte Referenzen

Dokumente verweisen über semantische Codes statt über UUIDs aufeinander.

// airport / JFK
{ "code": "JFK", "name": "John F. Kennedy International", "countryCode": "us" }

// country / us
{ "code": "us", "name": "United States", "currencyCode": "usd" }

// currency / usd
{ "code": "usd", "symbol": "$", "name": "US Dollar" }
// Kette Flughafen → Land → Währung
documents.get("currency",
  documents.get("country",
    documents.get("airport", meta.params.code).countryCode
  ).currencyCode
).symbol
// Für JFK: Gibt "$" zurück

Muster 3: Selbstreferenz mit dem doc-Kontext

Verwenden Sie doc für berechnete Felder, die anhand der Werte des aktuellen Dokuments auf andere Dokumente verweisen.

// In einem Produktdokument Details der zugehörigen Kategorie abrufen
documents.get("category", doc.categoryId).description

// Berechnete Versandkosten anhand des Ursprungslandes des Produkts
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight

Muster 4: Bidirektionale Referenzen

Wenn Dokumente aufeinander verweisen, achten Sie auf die Abruflimits.

// Autor des Artikels und anschließend weitere Artikel des Autors abrufen (Abrufanzahl beachten!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })

Muster 5: Polymorphe Referenzen

Wenn ein Feld auf unterschiedliche Schemata verweisen kann:

// content-block / hero-1
{ "type": "hero", "sourceType": "article", "sourceId": "welcome-post" }

// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Dynamische Schemaabfrage anhand von sourceType
documents.get("content-block", "hero-1").sourceType == "article"
  ? documents.get("article", documents.get("content-block", "hero-1").sourceId).headline
  : documents.get("product", documents.get("content-block", "hero-1").sourceId).name

Abhängigkeitsverfolgung

Jeder Aufruf von documents.get(), documents.find() und documents.ref().get() wird für die Cache-Invalidierung verfolgt. Wenn sich ein referenziertes Dokument ändert, weiß das CMS, welche CEL-Ausdrücke neu ausgewertet werden müssen.

Verfolgte Abhängigkeiten umfassen:

  • get: schema:identifier – Abhängigkeit von einem bestimmten Dokument
  • ref: schema:identifier – Wie get, über verkettete Syntax
  • query: schema:* – Abhängigkeit auf Schemaebene (jedes Dokument im Schema)

Best Practices für Referenzen

  1. Kettentiefe minimieren – Jede Ebene erhöht Latenz und Abrufanzahl.
  2. Zwischenergebnisse zwischenspeichern – Wenn Sie denselben verschachtelten Wert zweimal benötigen, rufen Sie das übergeordnete Dokument nur einmal ab.
  3. Nullprüfungen verwenden – Referenzen können fehlschlagen, wenn Dokumente gelöscht wurden.
  4. Codes gegenüber UUIDs bevorzugen – Codes sind in Ausdrücken lesbarer und umgebungsübergreifend stabil.
  5. Abruflimits beachten – Komplexe Referenzketten können das Limit von 50 Abrufen schnell erreichen.
// Schlecht: Ruft dasselbe Dokument zweimal ab
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio

// Besser: Mit einer Bedingung nur einmal prüfen
documents.get("article", "intro").authorId != null
  ? documents.get("author", documents.get("article", "intro").authorId).name
  : "Unknown Author"

Anhang A: Vollständiges Beispiel einer parametrischen Route

Dieser Ablauf erstellt eine mehrsprachige Landingpage, die unter /{lang}/landingPage erreichbar ist.

Schritt 1: Dokumentschema für Begrüßungen erstellen

Erstellen Sie im CMS-Administrationsbereich ein benutzerdefiniertes Schema namens greeting:

{
  "name": "greeting",
  "fields": [
    { "name": "code", "type": "string", "required": true },
    { "name": "headline", "type": "string", "required": true },
    { "name": "subheadline", "type": "string" },
    { "name": "ctaText", "type": "string" },
    { "name": "ctaUrl", "type": "string" }
  ]
}

Schritt 2: Begrüßungsdokumente erstellen

Erstellen Sie für jede Sprache ein Dokument:

Dokument: greeting/ko

{
  "code": "ko",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Get Started",
  "ctaUrl": "/ko/get-started"
}

Dokument: greeting/en

{
  "code": "en",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Get Started",
  "ctaUrl": "/en/get-started"
}

Dokument: greeting/ja

{
  "code": "ja",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Start",
  "ctaUrl": "/ja/get-started"
}

Schritt 3: Seite erstellen

Erstellen Sie eine Seite mit der folgenden Konfiguration:

  • Pfad/Muster: /{lang}/landingPage
  • Status: Live
  • Zuordnungen dynamischer Segmente: lang → die Komponente language zuordnen
  {
    "lang": "language"
  }

Schritt 4: Blöcke mit CEL-Skripten hinzufügen

Fügen Sie der Route einen Hero-Block hinzu und verwenden Sie für jedes Feld die folgenden CEL-Skripte:

Feld „Überschrift“:

documents.get("greeting", meta.params.lang).headline

Feld „Unterüberschrift“:

documents.get("greeting", meta.params.lang).subheadline

Feld „CTA-Text“:

documents.get("greeting", meta.params.lang).ctaText

Feld „CTA-URL“:

documents.get("greeting", meta.params.lang).ctaUrl

Schritt 5: In Next.js verwenden

Fügen Sie eine Catch-all-Route hinzu. ParametricRoutePage löst die Seite auf, extrahiert meta.params aus der URL, wertet Ihre CEL-Bindings serverseitig aus und rendert jeden Block über Ihre Registry – Sie müssen den meta-Kontext nicht selbst erstellen und den Client auf niedriger Ebene nicht direkt aufrufen.

// app/[...slug]/page.tsx
import ParametricRoutePage from 'cms-renderer/lib/renderer';
import { registry } from '@/lib/registry';
import { cmsConfig } from '@/lib/cms-config';

export const dynamic = 'force-static';

interface PageProps {
  params: Promise<{ slug: string[] }>;
}

export default async function Page({ params }: PageProps) {
  const { slug } = await params;

  return (
    <ParametricRoutePage
      registry={registry}
      apiKey={cmsConfig.apiKey}
      websiteId={cmsConfig.websiteId}
      cmsUrl={cmsConfig.cmsUrl}
      params={Promise.resolve({ slug })}
    />
  );
}

Schritt 6: Routen testen

Besuchen Sie diese URLs, um lokalisierte Inhalte zu sehen:

URLErwartete Überschrift
/ko/landingPage환영
/en/landingPageWelcome
/ja/landingPageいらっしゃいませ

So funktioniert die Auflösung

Wenn ein Benutzer /ko/landingPage besucht:

  1. Routenabgleich: Das CMS gleicht das Muster /{lang}/landingPage ab.
  2. Parameterextraktion: meta.params.lang = "ko"
  3. Validierung: Das CMS validiert, dass "ko" im Schema language vorhanden ist.
  4. CEL-Auswertung: Skripte wie documents.get("greeting", meta.params.lang) werden zu koreanischen Inhalten aufgelöst.
  5. Antwort: Lokalisierte Blöcke werden an den Client zurückgegeben.

Anhang B: Technische Referenz

CelMeta-Schnittstelle (TypeScript)

interface CelMeta {
  /** Aktueller Lokalisierungscode (z. B. 'en-US') */
  locale: string;
  /** Aus der URL extrahierte Routenparameter */
  params: Record<string, string>;
  /** URL-Pfadsegmente */
  segments: string[];
  /** ID des aktuellen Dokuments (falls ein bestehendes Dokument bearbeitet wird) */
  docId: string | null;
  /** Titel des aktuellen Dokuments */
  title: string;
}

Algorithmus zur Parameterextraktion

Die Funktion extractParams verarbeitet URL-Pfade:

Muster: /{country}/{lang}/products
Pfad:   /us/en/products

Algorithmus:
1. Beide normalisieren (abschließende Schrägstriche entfernen)
2. In Segmente aufteilen: ["us", "en", "products"] und ["{country}", "{lang}", "products"]
3. Segmentanzahl abgleichen (muss identisch sein)
4. Jedes Segmentpaar prüfen:
   - Beginnt das Muster mit : oder {}, als Parameter extrahieren
   - Andernfalls muss es exakt übereinstimmen
5. Rückgabe: { country: "us", lang: "en" }

Unterstützte Formate für Parameterbindungen

// Einfache Bindung (verwendet das Feld "code" für die Suche)
{ "lang": "language" }

// Detaillierte Bindung (benutzerdefiniertes Slug-Feld)
{
  "lang": {
    "schemaName": "language",
    "slugField": "code"
  },
  "slug": {
    "schemaName": "article",
    "slugField": "slug"
  }
}

Priorität bei der Dokumentensuche

Beim Abruf über documents.get(schema, identifier) gilt folgende Reihenfolge:

  1. UUID-Übereinstimmung: Wenn der Bezeichner eine gültige UUID ist, wird anhand von id abgerufen.
  2. Code-Feld: Das Feld content.code wird geprüft.
  3. Slug-Feld: Das Feld content.slug wird geprüft.
  4. Titelübereinstimmung: Das Feld title wird geprüft.

Dadurch können Dokumente flexibel über jeden eindeutigen Bezeichner referenziert werden.

Continue Reading
Previous‹Einrichtung-Admin-Panel-ProxyNextProject Scaffolding›