Praktická príručka na písanie výrazov CEL v CMS.
Praktická príručka na písanie výrazov CEL v CMS.
CEL (Common Expression Language) je ľahký skriptovací jazyk zabudovaný do nášho CMS. Umožňuje písať dynamické výrazy, ktoré získavajú údaje z dokumentov, čítajú parametre URL a priebežne vypočítavajú hodnoty.
Čo sa stane pri spustení skriptu CEL:
Váš skript Stroj Výsledok
documents.get("article", "intro") --> Načíta z databázy --> { headline: "Welcome", body: "..." }
.headline --> Extrahuje pole --> "Welcome"
CEL si môžete predstaviť ako jazyk dotazov iba na čítanie. Nemôže meniť nič v databáze – iba číta údaje a vracia vypočítaný výsledok. Preto je bezpečné používať ho kdekoľvek v CMS.
Každý výraz CEL má prístup k trom veciam:
| Objekt | Čo predstavuje | Príklad |
|---|---|---|
documents | Načítanie ľubovoľného dokumentu z CMS | documents.get("country", "us") |
meta | Informácie o aktuálnej požiadavke (lokalizácia, parametre URL) | meta.locale, meta.params.slug |
schema | Definície polí aktuálneho dokumentu | schema.fields |
docPri písaní výrazov CEL v editore dokumentu môžete k hodnotám polí aktuálneho dokumentu pristupovať pomocou objektu doc. To umožňuje vytvárať vypočítavané polia a odkazy medzi poľami.
// Prístup k poľu price aktuálneho dokumentu
doc.price
// Výpočet súčtu z polí aktuálneho dokumentu
doc.price * doc.quantity
// Podmienka založená na stave aktuálneho dokumentu
doc.status == "published" ? doc.title : "Draft: " + doc.title
Objekt doc obsahuje všetky hodnoty polí upravovaného dokumentu. Je užitočný pri:
doc.price * doc.quantity),Najvýkonnejšou funkciou CEL je načítavanie dokumentov z ľubovoľného miesta v CMS.
Syntax: documents.get(schemaName, identifier)
Predstavme si dokument article uložený s identifikátorom "welcome-post":
// Uložené v CMS ako: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Načítanie celého dokumentu:
documents.get("article", "welcome-post")
Výsledok:
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Načítanie iba nadpisu:
documents.get("article", "welcome-post").headline
Výsledok: "Welcome to Our Platform"
Načítanie autora:
documents.get("article", "welcome-post").author
Výsledok: "Sarah Chen"
Ak má stránka dynamické trasy, napríklad /articles/[slug], pomocou meta.params môžete získať parameter URL a načítať správny dokument.
Ak niekto navštívi /articles/welcome-post:
documents.get("article", meta.params.slug).headline
Výsledok: "Welcome to Our Platform"
Takto vytvoríte dynamické stránky – rovnaký skript CEL funguje pre každý článok a použije slug z URL.
Syntax: documents.find(schemaName) alebo documents.find(schemaName, filter)
// Získať všetky krajiny
documents.find("country")
Výsledok:
[
{ "code": "us", "name": "United States", "flag": "US" },
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
{ "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Získať krajiny pomocou filtra
documents.find("country", { "where": { "code": "us" } })
CEL podporuje načítavanie preloženého obsahu dokumentov dvoma spôsobmi: automatickým prekladom podľa lokalizácie a explicitným vyhľadaním prekladu.
Keď je nastavené meta.locale (napríklad z parametrov trasy alebo preferencií používateľa), documents.get() automaticky zlúči preložený obsah:
// Ak je meta.locale "fr", vráti francúzsky preklad zlúčený so základným dokumentom
documents.get("greeting", "welcome").headline
Ako to funguje:
meta.locale nie je "en" ani "en-US", vyhľadá preklad v tabuľke translations.{ ...baseContent, ...translatedContent }.Preložené polia teda prepíšu základné polia, zatiaľ čo nepreložené polia sa použijú zo základného dokumentu.
Ak potrebujete načítať konkrétny preklad bez ohľadu na aktuálnu lokalizáciu:
Syntax: documents.translated(schemaName, identifier, locale)
// Vždy načítať španielsky preklad
documents.translated("greeting", "welcome", "es").headline
// Načítať preklad podľa parametra URL
documents.translated("product", meta.params.id, meta.params.lang).description
// Porovnať preklady
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
Máte hero-block, ktorý má zobrazovať nadpis načítaný z dokumentu article.
{
"headline": "Build Faster, Ship Smarter",
"subheadline": "The modern CMS for developers"
}
Skript CEL v poli nadpisu hero bloku:
documents.get("article", "homepage-hero").headline
Výsledok: Hero zobrazí "Build Faster, Ship Smarter".
Vytvárate stránku /countries/[code] a chcete zobraziť úplný názov krajiny.
documents.get("country", meta.params.code).name
Pri návšteve /countries/us:
meta.params.code = "us""United States"Pri návšteve /countries/sa:
meta.params.code = "sa""Saudi Arabia"Zobrazte rôzne nadpisy podľa lokalizácie používateľa:
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
Ak je lokalizácia "ar-SA": výsledok je "Welcome, everyone".
Akákoľvek iná lokalizácia: výsledok je "Welcome".
Dokument article obsahuje pole countryCode a chcete získať úplný názov krajiny.
documents.get("country", documents.get("article", "us-news").countryCode).name
Postup:
documents.get("article", "us-news") vráti článok s countryCode = "us"..countryCode extrahuje "us".documents.get("country", "us") vráti dokument krajiny..name extrahuje "United States".Výsledok: "United States"
Ak dokument nemusí existovať, môžete zadať náhradnú hodnotu:
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
Alebo skontrolovať, či existuje konkrétne pole:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Unknown Author"
Ak má článok štítky a chcete overiť, či obsahuje konkrétny štítok:
"featured" in documents.get("article", "welcome-post").tags
Výsledok: true, ak článok obsahuje štítok "featured".
Prvý štítok získate takto:
documents.get("article", "welcome-post").tags[0]
Počet štítkov:
size(documents.get("article", "welcome-post").tags)
Výsledok: 2.
Parametrické trasy sú základom dynamických lokalizovaných stránok. Keď definujete vzor trasy, napríklad /{lang}/landingPage, CMS extrahuje parametre z URL a sprístupní ich cez meta.params.
Dynamické segmenty sa definujú syntaxou :paramName alebo {paramName}:
| Vzor | Príklad URL | Extrahované parametre |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
Každý parameter trasy možno kvôli validácii prepojiť so schémou dokumentu:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
Toto prepojenie oznámi CMS, aby:
lang z URL,language,Konfigurácia trasy:
/{lang}/landingPage/{lang}/landingPage{ "lang": "language" }Skript CEL na načítanie lokalizovaného obsahu:
documents.get("greeting", meta.params.lang).headline
| URL | meta.params.lang | Výsledok |
|---|---|---|
/ko/landingPage | "ko" | "Welcome" |
/en/landingPage | "en" | "Welcome" |
/ja/landingPage | "ja" | "Welcome" |
Pre trasy /{country}/{lang}/products môžete použiť:
// Získať názov krajiny
documents.get("country", meta.params.country).name
// Získať zoznam produktov podľa krajiny
documents.find("product", { "where": { "country": meta.params.country } })
// Kombinovaný pozdrav špecifický pre krajinu a jazyk
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name
CMS overuje parametre hierarchicky: najprv country oproti schéme country, potom lang oproti schéme language a prípadne overí, či sa jazyk nachádza v poli country.languages[].
meta.segments poskytuje surovú cestu URL ako pole. Je užitočné pri pozičnom prístupe bez pomenovaných parametrov.
| Cesta URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| Prípad použitia | Najlepší prístup |
|---|---|
| Pomenované parametre zo vzoru trasy | meta.params.lang |
| Prístup podľa pozície | meta.segments[0] |
| Zistenie hĺbky cesty | size(meta.segments) |
| Kontrola, či cesta obsahuje segment | "admin" in meta.segments |
// Prvý segment, často kód jazyka
meta.segments[0]
// Kontrola hĺbky cesty
size(meta.segments) > 2 ? "deep" : "shallow"
// Kontrola sekcie administrácie
"admin" in meta.segments ? "admin mode" : "public mode"
// Náhradná hodnota, ak parameter nie je naviazaný
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
Objekt meta obsahuje kontext aktuálnej požiadavky:
| Vlastnosť | Typ | Opis |
|---|---|---|
meta.locale | string | Aktuálny kód lokalizácie, napr. "en-US" alebo "ar-SA" |
meta.params | Record<string, string> | Parametre trasy extrahované zo vzoru URL |
meta.segments | string[] | Cesta URL rozdelená na segmenty |
meta.docId | string | null | UUID aktuálneho dokumentu; pri novom dokumente je null |
meta.title | string | Názov aktuálneho dokumentu |
Kód lokalizácie používa formát BCP 47:
// Kontrola jazykov písaných sprava doľava
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// Získanie iba časti s jazykom
meta.locale.split("-")[0] // Nepodporované – použite meta.params.lang
Parametre trás sú vždy reťazce. CMS ich pred vyhodnotením overí oproti naviazaným schémam:
meta.params.lang // "ko"
meta.params.country // "us"
meta.params.slug // "welcome-post"
has(meta.params.category) // true/false
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
Surové segmenty URL ako pole:
meta.segments[0] // Prvý segment
meta.segments[1] // Druhý segment
size(meta.segments) // Počet segmentov
"products" in meta.segments // Obsahuje cesta "products"?
UUID aktuálneho dokumentu, užitočné pri odkazovaní na seba:
meta.docId != null ? "editing" : "creating new"
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
Názov aktuálneho dokumentu:
"Editing: " + meta.title
meta.title.contains("Draft") ? "work in progress" : "published"
Ak je schéma známa, ale identifikátor je dynamický, môžete použiť prehľadnejšiu syntax:
// Tradičný prístup
documents.get("airports", meta.params.code).name
// Použitie ref()
documents.ref("airports").get(meta.params.code).name
Oba zápisy sú rovnocenné, no ref() jasnejšie oddeľuje dynamickú časť.
documents.get("schema", "identifier") // Získať jeden dokument
documents.get("schema", "id").fieldName // Získať konkrétne pole
documents.find("schema") // Získať všetky dokumenty
documents.find("schema", { "where": {...}}) // Filtrovaný dotaz
documents.ref("schema").get(identifier) // Zreťazené vyhľadávanie
documents.translated("schema", "id", "fr") // Získať s explicitnou lokalizáciou
meta.locale // "en-US", "ar-SA" atď.
meta.params.xyz // Parameter URL s názvom "xyz"
meta.segments // Cesta URL ako pole: ["articles", "intro"]
meta.segments[0] // Prvý segment cesty
meta.docId // ID aktuálneho dokumentu alebo null
meta.title // Názov aktuálneho dokumentu
doc.fieldName // Hodnota poľa aktuálneho dokumentu v kontexte editora
// Porovnanie
== != < <= > >=
// Logika
&& || !
// Ternárny operátor
condition ? valueIfTrue : valueIfFalse
// Príslušnosť
"value" in listOrMap
size(list) // Počet položiek
size(string) // Dĺžka reťazca
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // Kontrola existencie vlastnosti
hasProperty(obj, "key") // Alternatívna kontrola kľúča objektu
Ak sa niečo pokazí, zobrazí sa jedna z týchto chýb:
| Chyba | Význam |
|---|---|
SYNTAX_ERROR | Preklep v skripte, napríklad chýbajúca úvodzovka alebo nesprávny operátor |
TYPE_ERROR | Kombinujete typy, ktoré spolu nefungujú |
RUNTIME_ERROR | Skript sa spustil, ale narazil na problém, napríklad nedefinovanú premennú |
FETCH_LIMIT_EXCEEDED | Načítavate priveľa dokumentov; maximum je 50 |
TIMEOUT | Skript trval príliš dlho; maximum je 5 sekúnd |
AST_DEPTH_EXCEEDED | Výraz je príliš hlboko vnorený; maximálna hĺbka je 50 |
SCRIPT_TOO_LONG | Skript prekračuje limit 5 000 znakov |
Stroj CEL je navrhnutý s ohľadom na rozšíriteľnosť. Medzi plánované možnosti patria:
// Budúce: volanie externých služieb cez MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// Budúce: generovanie obsahu pomocou AI
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"])
Tieto možnosti budú pridané prostredníctvom systému registrovaných funkcií a zachovajú spätnú kompatibilitu s existujúcimi skriptmi.
documents. alebo meta. a editor zobrazí dostupné možnosti.documents.get("schema", "id"), potom pridajte .fieldName.!= null ? ... : ....documents.get() alebo documents.find() sa započítava do limitu 50 načítaní.has(meta.params.category).Táto časť opisuje pokročilé vzory prepájania dokumentov a vytvárania relačných obsahových štruktúr.
Najjednoduchší prípad: jeden dokument odkazuje na iný pomocou identifikátora.
// Článok obsahuje ID autora; načítanie mena autora
documents.get("author", documents.get("article", "intro").authorId).name
Hlboké vzťahy môžete vytvárať reťazením viacerých vyhľadávaní:
// Letisko → krajina → región → kontinent
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
// Lokalizovaný názov krajiny pre letisko
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
Každé volanie documents.get(), documents.find() a documents.ref().get() sa sleduje kvôli invalidácii vyrovnávacej pamäte. Keď sa odkazovaný dokument zmení, CMS vie, ktoré výrazy CEL treba znova vyhodnotiť.
Tento postup vytvorí viacjazyčnú vstupnú stránku dostupnú na adrese /{lang}/landingPage.
V administrácii CMS vytvorte vlastnú schému s názvom 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" }
]
}
Vytvorte dokument pre každý jazyk. Do polí headline, subheadline, ctaText a ctaUrl vložte lokalizovaný obsah.
Vytvorte stránku s touto konfiguráciou:
/{lang}/landingPagelang na komponent language{
"lang": "language"
}
Do trasy pridajte hero blok s týmito skriptami:
// Nadpis
documents.get("greeting", meta.params.lang).headline
// Podnadpis
documents.get("greeting", meta.params.lang).subheadline
// Text CTA
documents.get("greeting", meta.params.lang).ctaText
// URL CTA
documents.get("greeting", meta.params.lang).ctaUrl
Pridajte catch-all trasu. ParametricRoutePage vyrieši stránku, extrahuje meta.params z URL, vyhodnotí väzby CEL na serveri a vykreslí každý blok prostredníctvom registra.
// 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 })}
/>
);
}
Navštívte tieto URL a zobrazte lokalizovaný obsah:
| URL | Očakávaný nadpis |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
Pri návšteve /ko/landingPage:
/{lang}/landingPage.meta.params.lang = "ko"."ko" existuje v schéme language.documents.get("greeting", meta.params.lang) načítajú kórejský obsah.interface CelMeta {
/** Aktuálny kód lokalizácie, napr. 'en-US' */
locale: string;
/** Parametre trasy extrahované z URL */
params: Record<string, string>;
/** Segmenty cesty URL */
segments: string[];
/** ID aktuálneho dokumentu pri úprave existujúceho dokumentu */
docId: string | null;
/** Názov aktuálneho dokumentu */
title: string;
}
Funkcia extractParams spracúva cesty URL:
Vzor: /{country}/{lang}/products
Cesta: /us/en/products
Algoritmus:
1. Normalizuje obe hodnoty (odstráni koncové lomky)
2. Rozdelí ich na segmenty
3. Porovná počet segmentov
4. Pre každý pár segmentov:
- Ak vzor začína znakom : alebo obsahuje {}, extrahuje parameter
- Inak sa musí presne zhodovať
5. Vráti: { country: "us", lang: "en" }
// Jednoduchá väzba (na vyhľadanie používa pole "code")
{ "lang": "language" }
// Podrobná väzba (vlastné pole slug)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
Pri načítavaní pomocou documents.get(schema, identifier) sa použije tento postup:
id.content.code.content.slug.title.To umožňuje flexibilné odkazovanie na dokumenty pomocou ľubovoľného jedinečného identifikátora.