Praktinis vadovas, kaip CMS sistemoje rašyti CEL išraiškas.
Praktinis vadovas, kaip CMS sistemoje rašyti CEL išraiškas.
CEL (angl. „Common Expression Language“) – lengva scenarijų kalba, integruota į mūsų CMS. Ji leidžia rašyti dinamines išraiškas, kurios gali gauti duomenis iš dokumentų, nuskaityti URL parametrus ir apskaičiuoti reikšmes vykdymo metu.
Kas vyksta vykdant CEL scenarijų:
Jūsų scenarijus Variklis Rezultatas
| | |
v v v
documents.get("article", "intro") --> Gauna iš duomenų bazės --> { headline: "Welcome", body: "..." }
.headline --> Išrenka lauką --> "Welcome"
CEL galima laikyti tik skaitymui skirta užklausų kalba. Ji negali keisti duomenų bazėje esančios informacijos – tik nuskaito duomenis ir grąžina apskaičiuotą rezultatą. Todėl ją saugu naudoti bet kurioje CMS vietoje.
Kiekviena CEL išraiška gali pasiekti tris objektus:
| Objektas | Kas tai | Pavyzdys |
|---|---|---|
documents | Gauna bet kurį CMS dokumentą | documents.get("country", "us") |
meta | Informacija apie dabartinę užklausą (lokalė, URL parametrai) | meta.locale, meta.params.slug |
schema | Dabartinio dokumento laukų apibrėžtys | schema.fields |
docRašydami CEL išraiškas dokumentų redaktoriuje, galite pasiekti dabartinio dokumento laukų reikšmes naudodami objektą doc. Taip galima kurti apskaičiuojamus laukus ir nuorodas tarp laukų.
// Pasiekti dabartinio dokumento kainos lauką
doc.price
// Apskaičiuoti sumą iš dabartinio dokumento laukų
doc.price * doc.quantity
// Sąlyga pagal dabartinio dokumento būseną
doc.status == "published" ? doc.title : "Draft: " + doc.title
Objekte doc yra visos redaguojamo dokumento laukų reikšmės. Tai naudinga:
doc.price * doc.quantity);Galingiausia CEL funkcija – dokumentų gavimas iš bet kurios CMS vietos.
Sintaksė: documents.get(schemaName, identifier)
Tarkime, turite article dokumentą, kurio identifikatorius yra "welcome-post":
// CMS saugoma kaip: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
Norėdami gauti visą dokumentą:
documents.get("article", "welcome-post")
Grąžinama: visas dokumento objektas.
Norėdami gauti tik antraštę:
documents.get("article", "welcome-post").headline
Grąžinama: "Welcome to Our Platform"
Norėdami gauti autorių:
documents.get("article", "welcome-post").author
Grąžinama: "Sarah Chen"
Kai puslapis turi dinaminius maršrutus, pavyzdžiui, /articles/[slug], naudokite meta.params, kad gautumėte URL parametrą ir pasirinktumėte tinkamą dokumentą.
Jei lankytojas atidaro /articles/welcome-post:
documents.get("article", meta.params.slug).headline
Grąžinama: "Welcome to Our Platform"
Taip kuriami dinaminiai puslapiai – tas pats CEL scenarijus veikia su bet kuriuo straipsniu, naudodamas URL esantį slug.
Sintaksė: documents.find(schemaName) arba documents.find(schemaName, filter)
// Gauti visas šalis
documents.find("country")
Grąžinamas masyvas:
[
{ "code": "us", "name": "United States", "flag": "US" },
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
{ "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Gauti šalis su filtru
documents.find("country", { "where": { "code": "us" } })
CEL palaiko išverstą dokumentų turinį dviem būdais: automatiniu vertimu pagal lokalę ir aiškia vertimo paieška.
Kai nustatyta meta.locale (pavyzdžiui, iš maršruto parametrų arba naudotojo nuostatų), documents.get() automatiškai sujungia išverstą turinį:
// Jei meta.locale yra "fr", grąžinama prancūziška versija, sujungta su baziniu dokumentu
documents.get("greeting", "welcome").headline
Veikimo principas:
meta.locale nėra "en" arba "en-US", vertimo ieškoma translations lentelėje.{ ...baseContent, ...translatedContent }.Išversti laukai pakeičia bazinius laukus, o neišversti laukai perimami iš bazinio dokumento.
Kai reikia gauti konkretų vertimą nepaisant dabartinės lokalės:
Sintaksė: documents.translated(schemaName, identifier, locale)
// Visada gauti vertimą į ispanų kalbą
documents.translated("greeting", "welcome", "es").headline
// Gauti vertimą pagal URL parametrą
documents.translated("product", meta.params.id, meta.params.lang).description
// Palyginti vertimus
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
// Bazinis dokumentas: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }
// Vertimas (kalba: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }
// Vertimas (kalba: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }
CEL scenarijai:
// Kai meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Grąžina: "Bienvenue"
// Aiškus vertimas į ispanų kalbą
documents.translated("greeting", "welcome", "es").headline
// Grąžina: "Bienvenido"
// Atsarginė logika, kai vertimo nėra
documents.translated("greeting", "welcome", meta.params.lang) != null
? documents.translated("greeting", "welcome", meta.params.lang).headline
: documents.get("greeting", "welcome").headline
Turite hero-block, kuris turi rodyti iš article dokumento paimtą antraštę.
CEL scenarijus hero bloko antraštės laukelyje:
documents.get("article", "homepage-hero").headline
Rezultatas: hero bloke rodoma "Build Faster, Ship Smarter".
Kuriate puslapį /countries/[code] ir norite rodyti visą šalies pavadinimą:
documents.get("country", meta.params.code).name
Kai lankytojas atidaro /countries/us, meta.params.code yra "us", o rezultatas – "United States".
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
Jei lokalė yra "ar-SA", grąžinama "Welcome, everyone"; kitu atveju – "Welcome".
documents.get("country", documents.get("article", "us-news").countryCode).name
Pirmiausia gaunamas straipsnis, tada išrenkamas countryCode, pagal jį gaunama šalis, o galiausiai išrenkamas jos name.
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
Arba tikrinkite konkretų lauką:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "Unknown Author"
"featured" in documents.get("article", "welcome-post").tags
Grąžina true, jei straipsnis turi žymą "featured".
documents.get("article", "welcome-post").tags[0]
Grąžina pirmą žymą, pavyzdžiui, "announcement".
size(documents.get("article", "welcome-post").tags)
Grąžina žymų skaičių.
Parametriniai maršrutai yra pagrindas kuriant dinaminius ir lokalizuotus puslapius. Apibrėžus tokį šabloną kaip /{lang}/landingPage, CMS iš URL išrenka parametrus ir pateikia juos per meta.params.
Maršrutuose dinaminiams segmentams apibrėžti naudojama sintaksė :paramName arba {paramName}:
| Šablonas | Pavyzdinis URL | Išrenkami parametrai |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
Kiekvienas maršruto parametras gali būti susietas su dokumento schema validacijai:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
CMS tada išrenka lang iš URL, patikrina jį pagal language schemą ir, jei reikšmė tinkama, pateikia visą dokumentą išspręstuose parametruose.
documents.get("greeting", meta.params.lang).headline
| URL | meta.params.lang | Rezultatas |
|---|---|---|
/ko/landingPage | "ko" | lokalizuota antraštė |
/en/landingPage | "en" | lokalizuota antraštė |
/ja/landingPage | "ja" | lokalizuota antraštė |
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
// Gauti šalies pavadinimą
documents.get("country", meta.params.country).name
// Gauti produktus pagal šalį
documents.find("product", { "where": { "country": meta.params.country } })
// Rodyti konkrečiai šaliai skirtą pasisveikinimą naudotojo kalba
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name
CMS parametrus tikrina hierarchiškai: pirmiausia country pagal country schemą, tada lang pagal language schemą ir, pasirinktinai, ar kalba yra šalies languages[] masyve.
meta.segments pateikia neapdorotą URL kelią kaip masyvą, todėl galima pasiekti segmentus pagal jų vietą.
| URL kelias | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| Naudojimo atvejis | Geriausias būdas |
|---|---|
| Pavadinti maršruto parametrai | meta.params.lang |
| Prieiga pagal poziciją | meta.segments[0] |
| Kelio gylio nustatymas | size(meta.segments) |
| Tikrinimas, ar kelias turi segmentą | "admin" in meta.segments |
// Gauti pirmą segmentą
a = meta.segments[0]
// Patikrinti kelio gylį
size(meta.segments) > 2 ? "deep" : "shallow"
// Patikrinti, ar esame administravimo dalyje
"admin" in meta.segments ? "admin mode" : "public mode"
// Atsarginis variantas, jei parametras nesusietas
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
meta objekte yra visas dabartinės užklausos kontekstas:
| Savybė | Tipas | Aprašymas |
|---|---|---|
meta.locale | string | Dabartinės lokalės kodas, pvz., "en-US" arba "ar-SA" |
meta.params | Record<string, string> | Iš URL šablono išrinkti maršruto parametrai |
meta.segments | string[] | Į segmentus suskaidytas URL kelias |
meta.docId | string | null | Dabartinio dokumento UUID; naujam dokumentui – null |
meta.title | string | Dabartinio dokumento pavadinimas |
Lokalės kodas atitinka BCP 47 formatą:
// Patikrinti RTL kalbas
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
// Kalbos daliai gauti naudokite meta.params.lang
Maršruto parametrai visada yra eilutės. Prieš vertinimą CMS juos patikrina pagal susietas schemas:
meta.params.lang
meta.params.country
meta.params.slug
has(meta.params.category)
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)
Neapdoroti URL segmentai masyve:
meta.segments[0]
meta.segments[1]
size(meta.segments)
"products" in meta.segments
Dabartinio dokumento UUID, naudingas nuorodoms į patį dokumentą:
meta.docId != null ? "editing" : "creating new"
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
Dabartinio dokumento pavadinimas:
"Editing: " + meta.title
meta.title.contains("Draft") ? "work in progress" : "published"
Kai schema žinoma, o identifikatorius yra dinaminis, sintaksę galima sutrumpinti:
// Įprastas būdas
documents.get("airports", meta.params.code).name
// Naudojant ref()
documents.ref("airports").get(meta.params.code).name
Abu būdai yra lygiaverčiai, tačiau ref() aiškiau išskiria dinaminę dalį.
documents.get("schema", "identifier") // Gauti vieną dokumentą
documents.get("schema", "id").fieldName // Gauti konkretų lauką
documents.find("schema") // Gauti visus dokumentus
documents.find("schema", { "where": {...}}) // Filtruota užklausa
documents.ref("schema").get(identifier) // Susieta paieška
documents.translated("schema", "id", "fr") // Gauti su nurodyta lokale
meta.locale // "en-US", "ar-SA" ir kt.
meta.params.xyz // URL parametras "xyz"
meta.segments // URL kelias kaip masyvas
meta.segments[0] // Pirmasis kelio segmentas
meta.docId // Dabartinio dokumento ID arba null
meta.title // Dabartinio dokumento pavadinimas
doc.fieldName // Dabartinio dokumento lauko reikšmė
== != < <= > >=
&& || !
condition ? valueIfTrue : valueIfFalse
"value" in listOrMap
size(list) // Elementų skaičius
size(string) // Eilutės ilgis
"text".startsWith("te") // true
"text".endsWith("xt") // true
"text".contains("ex") // true
has(object.property) // Patikrinti, ar egzistuoja savybė
hasProperty(obj, "key") // Alternatyvi rakto patikra
Jei kas nors nepavyksta, galite pamatyti vieną iš šių klaidų:
| Klaida | Ką ji reiškia |
|---|---|
SYNTAX_ERROR | Rašybos klaida scenarijuje: trūksta kabutės arba netinkamas operatorius |
TYPE_ERROR | Kartu naudojami nesuderinami tipai |
RUNTIME_ERROR | Scenarijus pradėtas vykdyti, bet įvyko problema, pvz., neapibrėžtas kintamasis |
FETCH_LIMIT_EXCEEDED | Gaunama per daug dokumentų; daugiausia 50 |
TIMEOUT | Scenarijus vykdomas per ilgai; daugiausia 5 sekundės |
AST_DEPTH_EXCEEDED | Išraiška per daug įdėta; didžiausias gylis – 50 |
SCRIPT_TOO_LONG | Scenarijus viršija 5000 simbolių ribą |
CEL variklis sukurtas taip, kad jį būtų galima plėsti. Ateityje planuojamos šios galimybės:
// Ateityje: išorinių paslaugų iškvietimas per MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
// Ateityje: DI pagrįstas turinio generavimas
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"])
Šios galimybės bus įtrauktos per registruotų funkcijų sistemą, išlaikant suderinamumą su esamais scenarijais.
documents. arba meta., ir redaktorius parodys galimas parinktis.documents.get("schema", "id"), tada pridėkite .fieldName.!= null ? ... : ....documents.get() ar documents.find() skaičiuojamas į 50 gavimų limitą.has(meta.params.category).Ši dalis aprašo pažangius dokumentų susiejimo ir reliacinių turinio struktūrų kūrimo būdus.
Paprasčiausias variantas – vienas dokumentas nurodo kitą pagal identifikatorių:
// Straipsnyje saugomas autoriaus ID; gaunamas autoriaus vardas
documents.get("author", documents.get("article", "intro").authorId).name
documents.get("country", documents.get("airport", meta.params.code).countryCode).name
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name
// Oro uostas → Šalis → Regionas → Žemynas
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
// Gauti lokalizuotą oro uosto šalies pavadinimą
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
Kiekvienas documents.get(), documents.find() ir documents.ref().get() iškvietimas registruojamas talpyklos negaliojimo stebėjimui. Pasikeitus susietam dokumentui, CMS žino, kurias CEL išraiškas reikia įvertinti iš naujo.
Šiame pavyzdyje sukuriamas daugiakalbis nukreipimo puslapis, pasiekiamas adresu /{lang}/landingPage.
CMS administravimo dalyje sukurkite pasirinktinę schemą greeting su laukais code, headline, subheadline, ctaText ir ctaUrl.
Sukurkite po dokumentą kiekvienai kalbai, pavyzdžiui, greeting/ko, greeting/en ir greeting/ja, su atitinkamomis antraštėmis, paantraštėmis, raginimo veikti tekstais ir URL.
Sukurkite puslapį su tokia konfigūracija:
/{lang}/landingPagelang su language komponentu.Hero bloko laukams naudokite:
documents.get("greeting", meta.params.lang).headline
documents.get("greeting", meta.params.lang).subheadline
documents.get("greeting", meta.params.lang).ctaText
documents.get("greeting", meta.params.lang).ctaUrl
Pridėkite visus maršrutus apimantį maršrutą. ParametricRoutePage išsprendžia puslapį, iš URL išrenka meta.params, serverio pusėje įvertina CEL susiejimus ir per registrą atvaizduoja blokus.
// 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 })}
/>
);
}
Apsilankykite šiuose URL ir patikrinkite lokalizuotą turinį:
| URL | Tikėtina antraštė |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
Kai naudotojas atidaro /ko/landingPage, CMS suderina maršrutą, nustato meta.params.lang = "ko", patikrina parametrą pagal language schemą, įvertina CEL scenarijus ir klientui grąžina lokalizuotus blokus.
interface CelMeta {
/** Dabartinės lokalės kodas, pvz., 'en-US' */
locale: string;
/** Iš URL išrinkti maršruto parametrai */
params: Record<string, string>;
/** URL kelio segmentai */
segments: string[];
/** Dabartinio dokumento ID, jei redaguojamas esamas dokumentas */
docId: string | null;
/** Dabartinio dokumento pavadinimas */
title: string;
}
Šablonas: /{country}/{lang}/products
Kelias: /us/en/products
Algoritmas:
1. Normalizuoti abu kelius (pašalinti pasviruosius brūkšnius pabaigoje)
2. Suskaidyti į segmentus
3. Palyginti segmentų skaičių
4. Kiekvienai segmentų porai:
- Jei šablonas prasideda : arba {}, išrinkti parametrą
- Kitu atveju segmentai turi sutapti tiksliai
5. Grąžinti: { country: "us", lang: "en" }
// Paprastas susiejimas (paieškai naudojamas "code" laukas)
{ "lang": "language" }
// Išsamus susiejimas (pasirinktinis slug laukas)
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
Naudojant documents.get(schema, identifier), taikoma tokia tvarka:
id.content.code laukas.content.slug laukas.title laukas.Tai leidžia lanksčiai nurodyti dokumentus naudojant bet kurį unikalų identifikatorių.