מדריך מעשי לכתיבת ביטויי CEL במערכת ניהול התוכן.
מדריך מעשי לכתיבת ביטויי CEL במערכת ניהול התוכן.
CEL (שפת ביטויים נפוצה) היא שפת סקריפטים קלת־משקל המובנית במערכת ניהול התוכן שלנו. היא מאפשרת לכתוב ביטויים דינמיים השואבים נתונים ממסמכים, קוראים פרמטרים מכתובות URL ומחשבים ערכים בזמן הריצה.
זה מה שקורה כאשר סקריפט CEL מופעל:
Your Script The Engine Result
| | |
v v v
documents.get("article", "intro") --> Fetches from database --> { headline: "Welcome", body: "..." }
.headline --> Extracts the field --> "Welcome"
חשבו על CEL כשפת שאילתות לקריאה בלבד. היא אינה יכולה לשנות דבר במסד הנתונים — היא רק קוראת נתונים ומחזירה תוצאה מחושבת. לכן בטוח להשתמש בה בכל מקום במערכת ניהול התוכן.
לכל ביטוי CEL יש גישה לשלושה דברים:
| אובייקט | מהו | דוגמה |
|---|---|---|
documents | אחזור כל מסמך ממערכת ניהול התוכן | documents.get("country", "us") |
meta | מידע על הבקשה הנוכחית (אזור, פרמטרים של כתובת URL) | meta.locale, meta.params.slug |
schema | הגדרות השדות של המסמך הנוכחי | schema.fields |
docבעת כתיבת ביטויי CEL בתוך עורך מסמכים, ניתן לגשת לערכי השדות של המסמך הנוכחי באמצעות האובייקט doc. כך ניתן ליצור שדות מחושבים והפניות בין שדות.
// Access current document's price field
doc.price
// Calculate total from current document fields
doc.price * doc.quantity
// Conditional based on current document status
doc.status == "published" ? doc.title : "Draft: " + doc.title
האובייקט doc מכיל את כל ערכי השדות של המסמך הנערך. הוא שימושי עבור:
doc.price * doc.quantity)התכונה החזקה ביותר של CEL היא היכולת לאחזר מסמכים מכל מקום במערכת ניהול התוכן.
תחביר: documents.get(schemaName, identifier)
נניח שיש לכם מסמך article המאוחסן עם המזהה "welcome-post":
// Stored in the CMS as: article / welcome-post
{
"headline": "Welcome to Our Platform",
"author": "Sarah Chen",
"body": "We're excited to announce...",
"tags": ["announcement", "news"]
}
אחזור המסמך כולו:
documents.get("article", "welcome-post")
אחזור הכותרת בלבד:
documents.get("article", "welcome-post").headline
אחזור המחבר:
documents.get("article", "welcome-post").author
כאשר לדף יש נתיבים דינמיים (כמו /articles/[slug]), ניתן להשתמש ב־meta.params כדי לקבל את פרמטר כתובת ה־URL ולאחזר את המסמך המתאים.
אם משתמש מבקר ב־ /articles/welcome-post:
documents.get("article", meta.params.slug).headline
כך בונים דפים דינמיים — אותו סקריפט CEL פועל עבור כל מאמר, תוך שימוש ב־slug המופיע בכתובת ה־URL.
תחביר: documents.find(schemaName) או documents.find(schemaName, filter)
// Get all countries
documents.find("country")
// Get countries with a filter
documents.find("country", { "where": { "code": "us" } })
CEL תומכת באחזור תוכן מתורגם של מסמכים בשתי דרכים: תרגום אוטומטי המבוסס על האזור, וחיפוש תרגום מפורש.
כאשר meta.locale מוגדר (למשל מפרמטרים של נתיב או מהעדפות המשתמש), documents.get() ממזגת אוטומטית תוכן מתורגם:
// If meta.locale is "fr", returns French translation merged with base document
documents.get("greeting", "welcome").headline
אופן הפעולה:
meta.locale אינו "en" או "en-US", חיפוש התרגום בטבלת translations{ ...baseContent, ...translatedContent }משמעות הדבר היא ששדות מתורגמים מחליפים את שדות הבסיס, ואילו שדות שלא תורגמו משתמשים בערכי המסמך הבסיסי.
כאשר יש צורך לאחזר תרגום מסוים בלי קשר לאזור הנוכחי:
תחביר: documents.translated(schemaName, identifier, locale)
// Always fetch Spanish translation
documents.translated("greeting", "welcome", "es").headline
// Fetch translation based on URL parameter
documents.translated("product", meta.params.id, meta.params.lang).description
// Compare translations
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title
יש לכם hero-block שאמור להציג כותרת שנלקחה ממסמך article.
documents.get("article", "homepage-hero").headline
תוצאה: ה־hero מציג את הכותרת מהמסמך.
בעת בניית דף בכתובת /countries/[code], ניתן להציג את השם המלא של המדינה:
documents.get("country", meta.params.code).name
meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"
documents.get("country", documents.get("article", "us-news").countryCode).name
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "Article Not Found"
"featured" in documents.get("article", "welcome-post").tags
documents.get("article", "welcome-post").tags[0]
size(documents.get("article", "welcome-post").tags)
נתיבים פרמטריים הם המפתח לבניית דפים דינמיים ומותאמים לשפה. כאשר מגדירים תבנית נתיב כמו /{lang}/landingPage, מערכת ניהול התוכן מחלצת את הפרמטרים מכתובת ה־URL והופכת אותם לזמינים באמצעות meta.params.
נתיבים משתמשים בתחביר :paramName או {paramName} כדי להגדיר מקטעים דינמיים:
| תבנית | כתובת URL לדוגמה | פרמטרים שחולצו |
|---|---|---|
/:lang/landingPage | /ko/landingPage | { lang: "ko" } |
/{country}/{lang}/products | /us/en/products | { country: "us", lang: "en" } |
/articles/:slug | /articles/welcome-post | { slug: "welcome-post" } |
כל פרמטר נתיב יכול להיות מקושר לסכמת מסמך לצורך אימות.
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
הקישור מורה למערכת:
lang מכתובת ה־URLlanguagedocuments.get("greeting", meta.params.lang).headline
| כתובת URL | meta.params.lang | תוצאה |
|---|---|---|
/ko/landingPage | "ko" | "Welcome" |
/en/landingPage | "en" | "Welcome" |
/ja/landingPage | "ja" | "Welcome" |
meta.segments מספק את נתיב ה־URL הגולמי כמערך, ושימושי כאשר נדרשת גישה לפי מיקום ללא פרמטרים בעלי שם.
| נתיב URL | meta.segments |
|---|---|
/articles/tech/ai-news | ["articles", "tech", "ai-news"] |
/ko/landingPage | ["ko", "landingPage"] |
/us/en/products/featured | ["us", "en", "products", "featured"] |
/ | [] |
| מקרה שימוש | הגישה המומלצת |
|---|---|
| פרמטרים בעלי שם מתבנית הנתיב | meta.params.lang |
| גישה לפי מיקום | meta.segments[0] |
| קבלת עומק הנתיב | size(meta.segments) |
| בדיקה אם הנתיב מכיל מקטע | "admin" in meta.segments |
meta.segments[0]
size(meta.segments) > 2 ? "deep" : "shallow"
"admin" in meta.segments ? "admin mode" : "public mode"
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
| מאפיין | סוג | תיאור |
|---|---|---|
meta.locale | string | קוד האזור הנוכחי, למשל "en-US", "ko-KR", "ar-SA" |
meta.params | Record<string, string> | פרמטרים של הנתיב שחולצו מתבנית כתובת ה־URL |
meta.segments | string[] | נתיב כתובת ה־URL המחולק למקטעים |
meta.docId | string \| null | מזהה ה־UUID של המסמך הנוכחי |
meta.title | string | כותרת המסמך הנוכחי |
קוד האזור פועל לפי תבנית BCP 47:
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
meta.locale.split("-")[0] // Not supported - use meta.params.lang instead
פרמטרים של נתיב הם תמיד מחרוזות. מערכת ניהול התוכן מאמתת אותם מול הסכמות המקושרות לפני ההערכה:
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)
מזהה ה־UUID של המסמך הנוכחי:
meta.docId != null ? "editing" : "creating new"
meta.docId != null ? documents.get("article", meta.docId).status : "draft"
כותרת המסמך הנוכחי:
"Editing: " + meta.title
meta.title.contains("Draft") ? "work in progress" : "published"
כאשר הסכמה ידועה אך המזהה דינמי, ניתן להשתמש בתחביר נקי יותר:
documents.get("airports", meta.params.code).name
documents.ref("airports").get(meta.params.code).name
שתי הצורות שקולות, אך ref() מבהירה יותר את החלק הדינמי.
documents.get("schema", "identifier")
documents.get("schema", "id").fieldName
documents.find("schema")
documents.find("schema", { "where": {...}})
documents.ref("schema").get(identifier)
documents.translated("schema", "id", "fr")
meta.locale
meta.params.xyz
meta.segments
meta.segments[0]
meta.docId
meta.title
doc.fieldName
== != < <= > >=
&& || !
condition ? valueIfTrue : valueIfFalse
"value" in listOrMap
size(list)
size(string)
"text".startsWith("te")
"text".endsWith("xt")
"text".contains("ex")
has(object.property)
hasProperty(obj, "key")
אם משהו משתבש, תופיע אחת מהשגיאות הבאות:
| שגיאה | משמעות |
|---|---|
SYNTAX_ERROR | שגיאת כתיב בסקריפט, כגון מרכאה חסרה או אופרטור שגוי |
TYPE_ERROR | שילוב בין סוגים שאינם תואמים |
RUNTIME_ERROR | הסקריפט הופעל אך נתקל בבעיה, כגון משתנה לא מוגדר |
FETCH_LIMIT_EXCEEDED | אחזור של יותר מדי מסמכים, המגבלה היא 50 |
TIMEOUT | הסקריפט ארך זמן רב מדי, המגבלה היא 5 שניות |
AST_DEPTH_EXCEEDED | הביטוי מקונן עמוק מדי, עומק מרבי: 50 |
SCRIPT_TOO_LONG | הסקריפט חורג ממגבלת 5,000 התווים |
מנוע CEL תוכנן להרחבה. היכולות המתוכננות לעתיד כוללות:
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)
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"])
יכולות אלה יתווספו באמצעות מערכת הפונקציות הרשומות, תוך שמירה על תאימות לאחור עם סקריפטים קיימים.
documents. או meta. והעורך יציג את האפשרויות הזמינותdocuments.get("schema", "id"), ולאחר מכן הוסיפו .fieldName!= null ? ... : ...documents.get() או ל־documents.find() נספרת במגבלת 50 האחזורי́םhas(meta.params.category) לפני הגישהסעיף זה עוסק בדפוסים מתקדמים לקישור מסמכים ולבניית מבני תוכן יחסיים.
הצורה הפשוטה ביותר: מסמך אחד מפנה למסמך אחר באמצעות מזהה.
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
documents.get("continent",
documents.get("region",
documents.get("country",
documents.get("airport", meta.params.code).countryCode
).regionCode
).continentCode
).name
documents.translated("country",
documents.get("airport", meta.params.code).countryCode,
meta.params.lang
).name
כל קריאה ל־documents.get(), ל־documents.find() ול־documents.ref().get() נרשמת לצורך ביטול מטמון. כאשר מסמך שאליו מפנים משתנה, מערכת ניהול התוכן יודעת אילו ביטויי CEL יש להעריך מחדש.
התלויות הנרשמות כוללות:
schema:identifier — תלות במסמך מסויםschema:identifier — זהה ל־get, באמצעות תחביר משורשרschema:* — תלות ברמת הסכמה, כלומר כל מסמך בסכמהמדריך זה יוצר דף נחיתה רב־לשוני הזמין בכתובת /{lang}/landingPage.
בממשק הניהול של מערכת ניהול התוכן, צרו סכמה מותאמת אישית בשם greeting.
צרו מסמך עבור כל שפה.
צרו דף עם התצורה הבאה:
/{lang}/landingPagelang אל רכיב languageהוסיפו בלוק hero לנתיב עם הסקריפטים הבאים:
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
הוסיפו נתיב catch-all. ParametricRoutePage פותר את הדף, מחלץ את meta.params מכתובת ה־URL, מעריך את קישורי CEL בצד השרת ומרנדר כל בלוק באמצעות הרישום שלכם.
בקרו בכתובות הבאות כדי לראות תוכן מותאם לשפה:
| כתובת URL | כותרת צפויה |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | ברוכים הבאים |
/ja/landingPage | いらっしゃいませ |
interface CelMeta {
/** Current locale code (e.g., 'en-US') */
locale: string;
/** Route parameters extracted from URL */
params: Record<string, string>;
/** URL path segments */
segments: string[];
/** Current document ID (if editing existing document) */
docId: string | null;
/** Current document title */
title: string;
}
הפונקציה extractParams מעבדת נתיבי URL:
Pattern: /{country}/{lang}/products
Path: /us/en/products
Algorithm:
1. Normalize both (remove trailing slashes)
2. Split into segments: ["us", "en", "products"] and ["{country}", "{lang}", "products"]
3. Match segment counts (must be equal)
4. For each segment pair:
- If pattern starts with : or {}, extract as param
- Otherwise, must match exactly
5. Return: { country: "us", lang: "en" }
{ "lang": "language" }
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
בעת אחזור באמצעות documents.get(schema, identifier):
idcontent.codecontent.slugtitleכך ניתן להפנות למסמכים בגמישות באמצעות כל מזהה ייחודי.