profound-logoProfound CMS
⌘K
Admin
Theme
DocsTutorialBlogPhilosophy
DocsTutorialBlogPhilosophy

Hybrid

Collection of Pages with ComponentsTypes of ComponentsSetup server sent events (SSE) content refetchInstall Profound CMS as a proxyCEL Scripting in Template BuilderProject ScaffoldingMedia Library

Headless

SnelstartSplit Screen JSON Component Builder with LLMComponent Zod Pull

REST API

REST API OverviewgetConnect your websitegetGET /routesgetGET /routegetGET /blocksgetGET /blocks/with-cel-cachegetHaal gegenereerde blokken opgetGET /componentsgetGET /components/{name}getGET /dataset/{schema_name}getGET /content-changes (SSE)patchPATCH /dataset/{schema_name}postPOST /translationpatchPATCH /translationsgetGET /usagepostPOST /csvpatchPATCH /csv
All Systems Operational
Powered Byprofound-logo
Theme

CEL Scripting in Template Builder

Een praktische gids voor het schrijven van CEL-expressies in het CMS.

Een praktische gids voor het schrijven van CEL-expressies in het CMS.


Hoe CEL werkt

CEL (Common Expression Language) is een lichtgewicht scripttaal die in ons CMS is ingebouwd. Hiermee kun je dynamische expressies schrijven die gegevens uit documenten ophalen, URL-parameters lezen en ter plekke waarden berekenen.

Dit gebeurt er wanneer een CEL-script wordt uitgevoerd:

Jouw script                    De engine                     Resultaat
    |                              |                              |
    v                              v                              v
documents.get("article", "intro") --> Haalt op uit de database --> { headline: "Welkom", body: "..." }
         .headline                --> Extraheert het veld    --> "Welkom"

Zie CEL als een alleen-lezen querytaal. Het kan niets in de database aanpassen: het leest enkel gegevens en geeft een berekend resultaat terug. Hierdoor is het veilig om overal in het CMS te gebruiken.


De bouwstenen

Elke CEL-expressie heeft toegang tot drie zaken:

ObjectWat het isVoorbeeld
documentsHaal elk document uit het CMSdocuments.get("country", "us")
metaInformatie over het huidige verzoek (locale, URL-params)meta.locale, meta.params.slug
schemaDe velddefinities van het huidige documentschema.fields

Zelfverwijzing met doc

Wanneer je CEL-expressies schrijft binnen een documenteditor, kun je de veldwaarden van het huidige document benaderen via het doc-object. Hierdoor worden berekende velden en kruisverwijzingen tussen velden mogelijk.

// Toegang tot het prijsveld van het huidige document
doc.price

// Totaal berekenen op basis van velden in het huidige document
doc.price * doc.quantity

// Voorwaarde op basis van de status van het huidige document
doc.status == "published" ? doc.title : "Concept: " + doc.title

Het doc-object bevat alle veldwaarden van het document dat je bewerkt. Dit is nuttig voor:

  • Berekende velden (bijv. doc.price * doc.quantity)
  • Conditionele weergavelogica op basis van de documentstatus
  • Validatieachtige expressies

Documenten ophalen

De krachtigste functie van CEL is het ophalen van documenten overal uit je CMS.

Eén document ophalen

Syntaxis: documents.get(schemaName, identifier)

Stel, je hebt een article-document met de identifier "welcome-post" opgeslagen:

// Opgeslagen in het CMS als: article / welcome-post
{
  "headline": "Welkom op ons platform",
  "author": "Sarah Chen",
  "body": "We zijn verheugd aan te kondigen...",
  "tags": ["announcement", "news"]
}

Om het hele document op te halen:

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

Geeft terug:

{
  "headline": "Welkom op ons platform",
  "author": "Sarah Chen",
  "body": "We zijn verheugd aan te kondigen...",
  "tags": ["announcement", "news"]
}

Om alleen de headline op te halen:

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

Geeft terug: "Welkom op ons platform"

Om de auteur op te halen:

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

Geeft terug: "Sarah Chen"


URL-parameters gebruiken

Wanneer je pagina dynamische routes heeft (zoals /articles/[slug]), kun je meta.params gebruiken om de URL-parameter op te halen en het juiste document te laden.

Als iemand /articles/welcome-post bezoekt:

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

Geeft terug: "Welkom op ons platform"

Zo bouw je dynamische pagina's: hetzelfde CEL-script werkt voor elk artikel, en gebruikt simpelweg de slug uit de URL.


Meerdere documenten ophalen

Syntaxis: documents.find(schemaName) of documents.find(schemaName, filter)

// Haal alle landen op
documents.find("country")

Geeft terug:

[
  { "code": "us", "name": "Verenigde Staten", "flag": "US" },
  { "code": "sa", "name": "Saoedi-Arabië", "flag": "SA" },
  { "code": "gb", "name": "Verenigd Koninkrijk", "flag": "GB" }
]
// Haal landen op met een filter
documents.find("country", { "where": { "code": "us" } })

Geeft terug:

[
  { "code": "us", "name": "Verenigde Staten", "flag": "US" }
]

Vertalingen

CEL ondersteunt het ophalen van vertaalde documentinhoud op twee manieren: automatische vertaling op basis van locale en expliciete vertaalopvraging.

Automatische vertaling via meta.locale

Wanneer meta.locale is ingesteld (bijv. via routeparameters of gebruikersvoorkeuren), voegt documents.get() automatisch vertaalde inhoud samen:

// Als meta.locale "fr" is, wordt de Franse vertaling samengevoegd met het basisdocument
documents.get("greeting", "welcome").headline

Hoe het werkt:

  1. De basisinhoud van het document wordt opgehaald
  2. Als meta.locale niet "en" of "en-US" is, wordt de vertaling opgezocht in de tabel translations
  3. Vertaald velden worden over de basisinhoud geplaatst: { ...baseContent, ...translatedContent }

Dit betekent dat vertaalde velden de basisvelden overschrijven, terwijl onvertaalde velden terugvallen op het basisdocument.

Expliciete vertaling met documents.translated()

Voor situaties waarin je een specifieke vertaling nodig hebt, ongeacht de huidige locale:

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

// Altijd de Spaanse vertaling ophalen
documents.translated("greeting", "welcome", "es").headline

// Vertaling ophalen op basis van URL-parameter
documents.translated("product", meta.params.id, meta.params.lang).description

// Vertalingen vergelijken
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title

Vertalingsvoorbeeld

Je greeting-documenten met vertalingen:

// Basisdocument: greeting / welcome
{ "headline": "Welkom", "subheadline": "Welkom op ons platform" }

// Vertaling (taal: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }

// Vertaling (taal: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }

CEL-scripts:

// Met meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Geeft terug: "Bienvenue"

// Expliciete Spaanse vertaling
documents.translated("greeting", "welcome", "es").headline
// Geeft terug: "Bienvenido"

// Fallback-patroon voor ontbrekende vertalingen
documents.translated("greeting", "welcome", meta.params.lang) != null
  ? documents.translated("greeting", "welcome", meta.params.lang).headline
  : documents.get("greeting", "welcome").headline

Voorbeelden uit de praktijk

Voorbeeld 1: Titel van hero-blok uit een ander document

Je hebt een hero-block dat een headline moet tonen uit een article-document.

Je artikeldocument (identifier: "homepage-hero"):

{
  "headline": "Bouw sneller, lever slimmer",
  "subheadline": "Het moderne CMS voor ontwikkelaars"
}

CEL-script in het titelfeld van het hero-blok:

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

Resultaat: De hero toont "Bouw sneller, lever slimmer"


Voorbeeld 2: Landnaam op basis van code

Je bouwt een pagina op /countries/[code] en wilt de volledige landnaam tonen.

Je landdocumenten:

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

// country / sa
{ "code": "sa", "name": "Saoedi-Arabië", "flag": "SA", "languages": ["ar", "en"] }

CEL-script:

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

Wanneer iemand /countries/us bezoekt:

  • meta.params.code = "us"
  • Resultaat: "Verenigde Staten"

Wanneer iemand /countries/sa bezoekt:

  • meta.params.code = "sa"
  • Resultaat: "Saoedi-Arabië"

Voorbeeld 3: Conditionele inhoud op basis van locale

Toon verschillende headlines op basis van de locale van de gebruiker.

meta.locale == "ar-SA" ? "Welkom, iedereen" : "Welkom"

Als locale "ar-SA" is: Geeft "Welkom, iedereen" Als locale iets anders is: Geeft "Welkom"


Voorbeeld 4: Gekoppelde documentlookups

Je article heeft een veld countryCode, en je wilt de volledige landnaam ophalen.

Artikeldocument:

{ "headline": "Nieuws uit de VS", "countryCode": "us" }

CEL-script:

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

Wat er gebeurt:

  1. documents.get("article", "us-news") geeft { "headline": "Nieuws uit de VS", "countryCode": "us" }
  2. .countryCode haalt "us" op
  3. documents.get("country", "us") geeft { "code": "us", "name": "Verenigde Staten", ... }
  4. .name haalt "Verenigde Staten" op

Resultaat: "Verenigde Staten"


Voorbeeld 5: Fallbackwaarden

Als een document mogelijk niet bestaat, kun je een fallback bieden:

documents.get("article", meta.params.slug) != null
  ? documents.get("article", meta.params.slug).headline
  : "Artikel niet gevonden"

Of controleer of een specifiek veld bestaat:

documents.get("article", "intro").author != null
  ? documents.get("article", "intro").author
  : "Onbekende auteur"

Voorbeeld 6: Werken met lijsten

Je artikel heeft tags en je wilt controleren of een specifieke tag bestaat:

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

Geeft: true als het artikel de tag "featured" heeft

De eerste tag ophalen:

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

Geeft: "announcement" (de eerste tag)

De tags tellen:

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

Geeft: 2 (aantal tags)


Parametrische routes en meta.params

Parametrische routes zijn de sleutel tot het bouwen van dynamische, gelokaliseerde pagina's. Wanneer je een routepatroon zoals /{lang}/landingPage definieert, haalt het CMS parameters uit de URL en stelt ze beschikbaar via meta.params.

Hoe routeparameters werken

Routepatroonomschrijving: Routes gebruiken de syntaxis :paramName of {paramName} om dynamische segmenten te definiëren:

PatroonVoorbeeld-URLExtracted params
/:lang/landingPage/ko/landingPage{ lang: "ko" }
/{country}/{lang}/products/us/en/products{ country: "us", lang: "en" }
/articles/:slug/articles/welcome-post{ slug: "welcome-post" }

Parameterkoppelingen: Elk routeparameter kan aan een documentschema worden gekoppeld voor validatie:

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

Deze koppeling vertelt het CMS:

  1. Haal het segment lang uit de URL
  2. Valideer het tegen het schema language (zoekt een document waarbij content.code overeenkomt)
  3. Maak bij geldigheid het volledige document beschikbaar in de opgeloste parameters

Voorbeeld: Landing page op basis van taal

Routeconfiguratie:

  • Pad: /{lang}/landingPage
  • Patroon: /{lang}/landingPage
  • Parameterkoppelingen: { "lang": "language" }

Je greeting-documenten:

// greeting / ko
{ "code": "ko", "headline": "Welkom", "subheadline": "Welkom op ons platform", "ctaText": "Aan de slag", "ctaUrl": "/ko/get-started" }

// greeting / en
{ "code": "en", "headline": "Welkom", "subheadline": "Welkom op ons platform", "ctaText": "Aan de slag", "ctaUrl": "/en/get-started" }

// greeting / ja
{ "code": "ja", "headline": "Welkom", "subheadline": "Welkom op ons platform", "ctaText": "Start", "ctaUrl": "/ja/get-started" }

CEL-script om gelokaliseerde inhoud op te halen:

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

Hoe dit wordt opgelost:

URLmeta.params.langResultaat
/ko/landingPage"ko""Welkom"
/en/landingPage"en""Welkom"
/ja/landingPage"ja""Welkom"

Geavanceerd patroon: routes land + taal

Voor routes zoals /{country}/{lang}/products:

Routeconfiguratie:

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

CEL-scripts:

// Landnaam ophalen
documents.get("country", meta.params.country).name

// Gelokaliseerde productlijst op basis van land
documents.find("product", { "where": { "country": meta.params.country } })

// Gecombineerd: toon land-specifieke begroeting in de taal van de gebruiker
documents.get("greeting", meta.params.lang).headline + " uit " + documents.get("country", meta.params.country).name

Validatiekaskade: Het CMS valideert parameters hiërarchisch. Voor routes /{country}/{lang}:

  1. Valideert de parameter country tegen het schema country
  2. Valideert de parameter lang tegen het schema language
  3. Valideert optioneel dat lang voorkomt in de array country.languages[] (hiërarchische validatie)

meta.segments - ruwe toegang tot het URL-pad

meta.segments geeft het ruwe URL-pad als een array, handig wanneer je positionele toegang nodig hebt zonder benoemde parameters.

Hoe het werkt:

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

Wanneer meta.segments in plaats van meta.params gebruiken

Use caseBeste aanpak
Benoemde parameters uit het routepatroonmeta.params.lang
Toegang op basis van positiemeta.segments[0]
Pad-diepte ophalensize(meta.segments)
Controleren of het pad een segment bevat"admin" in meta.segments

Voorbeelden met meta.segments

// Eerste segment ophalen (vaak taalcode)
meta.segments[0]

// Pad-diepte controleren
size(meta.segments) > 2 ? "diep" : "oppervlakkig"

// Controleren of we in het beheergedeelte zitten
"admin" in meta.segments ? "adminmodus" : "openbare modus"

// Fallback: gebruik segment als param niet is gekoppeld
has(meta.params.lang) ? meta.params.lang : meta.segments[0]

Volledige referentie van het meta-object

Het meta-object bevat alle context over het huidige verzoek:

EigenschapTypeBeschrijving
meta.localestringHuidige localecode (bijv. "en-US", "ko-KR", "ar-SA")
meta.paramsRecord<string, string>Routeparameters die uit het URL-patroon zijn gehaald
meta.segmentsstring[]URL-pad opgesplitst in segmenten
meta.docIdstring \| nullUUID van het huidige document (null voor nieuwe documenten)
meta.titlestringTitel van het huidige document

meta.locale

De localecode volgt de BCP 47-notatie (taal-regio):

// Locale controleren voor RTL-talen
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"

// Alleen het taalgedeelte ophalen
meta.locale.split("-")[0]  // Niet ondersteund - gebruik in plaats daarvan meta.params.lang

meta.params

Routeparameters zijn altijd strings. Het CMS valideert ze tegen gekoppelde schema's vóór evaluatie:

// Benoemde parameter benaderen
meta.params.lang           // "ko"
meta.params.country        // "us"
meta.params.slug           // "welcome-post"

// Controleren of parameter bestaat
has(meta.params.category)  // true/false

// Gebruiken in documentopvraag
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)

meta.segments

Ruwe URL-segmenten als array:

// Benader op index (0-based)
meta.segments[0]           // Eerste segment
meta.segments[1]           // Tweede segment

// Lengte controleren
size(meta.segments)        // Aantal segmenten

// Lidmaatschap controleren
"products" in meta.segments  // Bevat het pad "products"?

meta.docId

De UUID van het huidige document, handig voor zelfverwijzende scripts:

// Alleen beschikbaar bij het bewerken van bestaande documenten
meta.docId != null ? "bewerken" : "nieuw aanmaken"

// Gebruik in conditionele logica
meta.docId != null ? documents.get("article", meta.docId).status : "draft"

meta.title

De titel van het huidige document:

// Gebruik voor weergave
"Bewerken: " + meta.title

// Conditie op basis van titel
meta.title.contains("Draft") ? "werk in uitvoering" : "gepubliceerd"

documents.ref() - gekoppelde lookups

Voor leesbaardere syntaxis wanneer het schema bekend is maar de identifier dynamisch:

// Traditionele aanpak
documents.get("airports", meta.params.code).name

// Met ref() - schema apart van dynamische identifier
documents.ref("airports").get(meta.params.code).name

Beide zijn gelijkwaardig, maar ref() maakt het dynamische deel duidelijker.


Quick reference

Documenten ophalen

documents.get("schema", "identifier")       // Eén document ophalen
documents.get("schema", "id").fieldName     // Een specifiek veld ophalen
documents.find("schema")                    // Alle documenten ophalen
documents.find("schema", { "where": {...}}) // Gefilterde query
documents.ref("schema").get(identifier)     // Gekoppelde lookup
documents.translated("schema", "id", "fr")  // Ophalen met expliciete locale

Contextvariabelen

meta.locale          // "en-US", "ar-SA", enz.
meta.params.xyz      // URL-parameter met naam "xyz"
meta.segments        // URL-pad als array: ["articles", "intro"]
meta.segments[0]     // Eerste padsegment
meta.docId           // Huidige document-ID (of null)
meta.title           // Huidige documenttitel
doc.fieldName        // Veldwaarde van het huidige document (in editorcontext)

Operatoren

// Vergelijking
==  !=  <  <=  >  >=

// Logica
&&  ||  !

// Ternair (if-else)
voorwaarde ? waardeIndienWaar : waardeIndienOnwaar

// Lidmaatschap
"value" in listOrMap

Veelgebruikte functies

size(list)                    // Aantal items
size(string)                  // Stringlengte
"text".startsWith("te")       // true
"text".endsWith("xt")         // true
"text".contains("ex")         // true
has(object.property)          // Controleren of eigenschap bestaat
hasProperty(obj, "key")       // Controleren of object sleutel heeft (alternatieve syntaxis)

Foutmeldingen

Als er iets misgaat, zie je een van deze meldingen:

FoutBetekenis
SYNTAX_ERRORTypfout in je script (ontbrekend aanhalingsteken, verkeerde operator)
TYPE_ERRORJe combineert typen die niet op elkaar passen
RUNTIME_ERRORHet script draaide, maar liep tegen een probleem aan (ongedefinieerde variabele)
FETCH_LIMIT_EXCEEDEDJe haalt te veel documenten op (max. 50)
TIMEOUTScript duurde te lang (max. 5 seconden)
AST_DEPTH_EXCEEDEDExpressie te diep genest (maximale diepte: 50)
SCRIPT_TOO_LONGScript overschrijdt limiet van 5000 tekens

Uitbreidbaarheid en toekomstige mogelijkheden

De CEL-engine is ontworpen voor uitbreidbaarheid. Toekomstige mogelijkheden omvatten:

Gepland: MCP-serverintegratie

// Toekomst: externe services aanroepen via MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)

Gepland: AI-mogelijkheden

// Toekomst: door AI aangestuurde contentgeneratie
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"])

Deze mogelijkheden worden toegevoegd via het systeem met geregistreerde functies, waarbij achterwaartse compatibiliteit behouden blijft.


Tips

  1. Gebruik autocomplete - Typ documents. of meta. en de editor toont beschikbare opties
  2. Begin eenvoudig - Test eerst met documents.get("schema", "id") en voeg daarna .fieldName toe
  3. Controleer op null - Als een document mogelijk niet bestaat, voeg een fallback toe met != null ? ... : ...
  4. Haalt niet te veel op - Elke documents.get() of documents.find() telt mee voor de limiet van 50
  5. Verkies meta.params boven meta.segments - Benoemde parameters zijn gevalideerd en betrouwbaarder
  6. Gebruik has() voor optionele parameters - Controleer has(meta.params.category) voordat je deze gebruikt
  7. Gebruik documents.ref() voor dynamische identifiers - Duidelijkere syntaxis wanneer het schema vaststaat maar de identifier dynamisch is
  8. Gebruik doc.fieldName voor zelfverwijzingen - Benader velden van het huidige document binnen berekende expressies

Document-naar-documentverwijzingen

Deze sectie behandelt geavanceerde patronen om documenten aan elkaar te koppelen en relationele contentstructuren te bouwen.

Basisverwijspatroon

De eenvoudigste vorm: één document verwijst naar een ander via een identifier.

// Artikel slaat auteur-ID op, haal naam van auteur op
documents.get("author", documents.get("article", "intro").authorId).name

Gekoppelde lookups met documents.ref()

Voor duidelijkere syntaxis wanneer de identifier dynamisch is:

// Traditionele aanpak
documents.get("country", documents.get("airport", meta.params.code).countryCode).name

// Met ref() - duidelijker wanneer schema bekend is maar identifier dynamisch
\ndocuments.ref("country").get(documents.get("airport", meta.params.code).countryCode).name

Meerlagige verwijsketens

Bouw diepgaande relaties door meerdere lookups te koppelen:

// Airport → Country → Region → Continent
documents.get("continent",
  documents.get("region",
    documents.get("country",
      documents.get("airport", meta.params.code).countryCode
    ).regionCode
  ).continentCode
).name

Verwijzing met vertaling

Combineer documentverwijzingen met vertalingen:

// Gelokaliseerde landnaam voor een luchthaven ophalen
documents.translated("country",
  documents.get("airport", meta.params.code).countryCode,
  meta.params.lang
).name

Verwijspatronen per use case

Patroon 1: Foreign key lookup

Document slaat een ID op dat naar een ander document verwijst.

// article / tech-news
{ "title": "Tech-update", "authorId": "author-123", "categoryId": "cat-tech" }
// Naam van auteur ophalen
documents.get("author", documents.get("article", meta.params.slug).authorId).name

// Categorie ophalen met fallback
documents.get("article", meta.params.slug).categoryId != null
  ? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
  : "Geen categorie"

Patroon 2: Verwijzingen op basis van codes

Documenten verwijzen naar elkaar via semantische codes in plaats van UUID's.

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

// country / us
{ "code": "us", "name": "Verenigde Staten", "currencyCode": "usd" }

// currency / usd
{ "code": "usd", "symbol": "$", "name": "Amerikaanse dollar" }
// Airport → Country → Currency-keten
documents.get("currency",
  documents.get("country",
    documents.get("airport", meta.params.code).countryCode
  ).currencyCode
).symbol
// Voor JFK: geeft "$"

Patroon 3: Zelfverwijzing met doc-context

Gebruik doc voor berekende velden die andere documenten ophalen op basis van de waarden van het huidige document.

// In een productdocument, haal gerelateerde categoriedetails op
documents.get("category", doc.categoryId).description

// Berekende verzendkosten op basis van herkomstland van product
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight

Patroon 4: Bidirectionele verwijzingen

Wanneer documenten naar elkaar verwijzen, let op het aantal opvragingen.

// Haal auteur van artikel op en daarna andere artikelen van dezelfde auteur (let op fetch-telling)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })

Patroon 5: Polymorfe verwijzingen

Wanneer een veld naar verschillende schema's kan verwijzen:

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

// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Dynamische schema-opvraging op basis van 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

Dependency-tracking

Elke documents.get(), documents.find() en documents.ref().get()-aanroep wordt gevolgd voor cache-invalidatie. Wanneer een verwezen document verandert, weet het CMS welke CEL-expressies opnieuw geëvalueerd moeten worden.

Bijgehouden afhankelijkheden omvatten:

  • get: schema:identifier - Specifieke documentafhankelijkheid
  • ref: schema:identifier - Zelfde als get, via gekoppelde syntaxis
  • query: schema:* - Schema-afhankelijkheid (elk document in het schema)

Best practices voor verwijzingen

  1. Beperk ketendiepte - Elke laag voegt latency en opvragingen toe
  2. Cache tussenresultaten - Als je dezelfde geneste waarde twee keer nodig hebt, haal het bovenliggende document één keer op
  3. Gebruik null-controles - Verwijzingen kunnen breken als documenten worden verwijderd
  4. Verkies codes boven UUID's - Codes zijn leesbaar in expressies en stabiel in verschillende omgevingen
  5. Let op opvraaglimieten - Complexe ketens kunnen snel de limiet van 50 opvragingen bereiken
// Slecht: haalt hetzelfde document twee keer op
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio

// Beter: gebruik conditionele controle om slechts één keer te halen
documents.get("article", "intro").authorId != null
  ? documents.get("author", documents.get("article", "intro").authorId).name
  : "Onbekende auteur"

Bijlage A: volledig voorbeeld van parametrische route

Deze walkthrough maakt een meertalige landing page beschikbaar op /{lang}/landingPage.

Stap 1: Maak het schema voor greeting-documenten

Maak in de CMS-beheeromgeving een aangepast schema met de naam 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" }
  ]
}

Stap 2: Maak greeting-documenten

Maak documenten voor elke taal:

Document: greeting/ko

{
  "code": "ko",
  "headline": "Welkom",
  "subheadline": "Welkom op ons platform",
  "ctaText": "Aan de slag",
  "ctaUrl": "/ko/get-started"
}

Document: greeting/en

{
  "code": "en",
  "headline": "Welkom",
  "subheadline": "Welkom op ons platform",
  "ctaText": "Aan de slag",
  "ctaUrl": "/en/get-started"
}

Document: greeting/ja

{
  "code": "ja",
  "headline": "Welkom",
  "subheadline": "Welkom op ons platform",
  "ctaText": "Start",
  "ctaUrl": "/ja/get-started"
}

Stap 3: Maak de pagina

Maak een pagina met de volgende configuratie:

  • Pad/patroon: /{lang}/landingPage
  • Status: Live
  • Mappings voor dynamische segmenten: koppel lang → de component language
  {
    "lang": "language"
  }

Stap 4: Voeg blokken toe met CEL-scripts

Voeg een hero-blok toe aan de route met deze CEL-scripts per veld:

Headline-veld:

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

Subheadline-veld:

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

CTA-tekstveld:

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

CTA-URL-veld:

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

Stap 5: Gebruik in Next.js

Voeg een catch-all route toe. ParametricRoutePage lost de pagina op, haalt meta.params uit de URL, evalueert je CEL-koppelingen server-side en rendert elk blok via je registry — je hoeft de meta-context of de low-level client niet zelf te bouwen.

// 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 })}
    />
  );
}

Stap 6: Test de routes

Bezoek deze URL's om gelokaliseerde inhoud te zien:

URLVerwachte headline
/ko/landingPage환영
/en/landingPageWelcome
/ja/landingPageいらっしゃいませ

Hoe de oplossing werkt

Wanneer een gebruiker /ko/landingPage bezoekt:

  1. Routematching: het CMS matcht het patroon /{lang}/landingPage
  2. Parameterextractie: meta.params.lang = "ko"
  3. Validatie: het CMS controleert of "ko" bestaat in het schema language
  4. CEL-evaluatie: scripts zoals documents.get("greeting", meta.params.lang) leveren Koreaanse content op
  5. Respons: gelokaliseerde blokken worden naar de client gestuurd

Bijlage B: technische referentie

CelMeta-interface (TypeScript)

interface CelMeta {
  /** Huidige localecode (bijv. 'en-US') */
  locale: string;
  /** Routeparameters die uit de URL zijn gehaald */
  params: Record<string, string>;
  /** URL-padsegmenten */
  segments: string[];
  /** Huidige document-ID (bij bewerken bestaand document) */
  docId: string | null;
  /** Huidige documenttitel */
  title: string;
}

Parameter-extractie-algoritme

De functie extractParams verwerkt URL-paden:

Patroon: /{country}/{lang}/products
Pad:     /us/en/products

Algoritme:
1. Normaliseer beide (verwijder afsluitende slashes)
2. Splits in segmenten: ["us", "en", "products"] en ["{country}", "{lang}", "products"]
3. Segmentaantal moet overeenkomen
4. Voor elk segmentpaar:
   - Als patroon begint met : of {}, extraheer als parameter
   - Anders moet het exact overeenkomen
5. Resultaat: { country: "us", lang: "en" }

Ondersteunde formats voor parameterkoppeling

// Eenvoudige koppeling (gebruikt veld "code" voor lookup)
{ "lang": "language" }

// Gedetailleerde koppeling (aangepast slug-veld)
{
  "lang": {
    "schemaName": "language",
    "slugField": "code"
  },
  "slug": {
    "schemaName": "article",
    "slugField": "slug"
  }
}

Prioriteit bij documentopzoeking

Bij het ophalen via documents.get(schema, identifier):

  1. UUID-match: als de identifier een geldige UUID is, ophalen op id
  2. Codeveld: controleer veld content.code
  3. Slugveld: controleer veld content.slug
  4. Titelmatch: controleer veld title

Zo kun je documenten flexibel refereren met elke unieke identifier.

Continue Reading
Previous‹Install Profound CMS as a proxyNextProject Scaffolding›