คู่มือเชิงปฏิบัติสำหรับการเขียนนิพจน์ CEL ใน CMS
คู่มือเชิงปฏิบัติสำหรับการเขียนนิพจน์ CEL ใน CMS
CEL (Common Expression Language) คือภาษาสคริปต์ขนาดเล็กที่มีอยู่ใน CMS ของเรา ช่วยให้คุณเขียนนิพจน์แบบไดนามิกเพื่อดึงข้อมูลจากเอกสาร อ่านพารามิเตอร์ URL และคำนวณค่าได้ทันที
เมื่อสคริปต์ CEL ทำงาน จะเกิดขั้นตอนดังนี้:
สคริปต์ของคุณ เอ็นจิน ผลลัพธ์
| | |
v v v
documents.get("article", "intro") --> ดึงข้อมูลจากฐานข้อมูล --> { headline: "Welcome", body: "..." }
.headline --> ดึงฟิลด์ --> "Welcome"
ให้คิดว่า CEL เป็นภาษาสำหรับค้นข้อมูลแบบอ่านอย่างเดียว ไม่สามารถแก้ไขข้อมูลในฐานข้อมูลได้ แต่จะอ่านข้อมูลและส่งคืนผลลัพธ์ที่คำนวณแล้ว จึงปลอดภัยสำหรับการใช้งานทุกที่ใน CMS
นิพจน์ CEL ทุกตัวสามารถเข้าถึงสิ่งต่าง ๆ ได้สามอย่าง:
| ออบเจ็กต์ | คืออะไร | ตัวอย่าง |
|---|---|---|
documents | ดึงเอกสารใด ๆ จาก CMS | documents.get("country", "us") |
meta | ข้อมูลของคำขอปัจจุบัน (locale และพารามิเตอร์ URL) | meta.locale, meta.params.slug |
schema | คำจำกัดความฟิลด์ของเอกสารปัจจุบัน | schema.fields |
docเมื่อเขียนนิพจน์ CEL ภายในตัวแก้ไขเอกสาร คุณสามารถเข้าถึงค่าฟิลด์ของเอกสารปัจจุบันผ่านออบเจ็กต์ doc ซึ่งช่วยให้สร้างฟิลด์คำนวณและการอ้างอิงข้ามฟิลด์ได้
// เข้าถึงฟิลด์ราคาในเอกสารปัจจุบัน
doc.price
// คำนวณยอดรวมจากฟิลด์ในเอกสารปัจจุบัน
doc.price * doc.quantity
// เงื่อนไขตามสถานะของเอกสารปัจจุบัน
doc.status == "published" ? doc.title : "ฉบับร่าง: " + doc.title
ออบเจ็กต์ doc ประกอบด้วยค่าฟิลด์ทั้งหมดจากเอกสารที่กำลังแก้ไข เหมาะสำหรับ:
doc.price * doc.quantityความสามารถที่ทรงพลังที่สุดของ CEL คือการดึงเอกสารจากที่ใดก็ได้ใน CMS
ไวยากรณ์: documents.get(schemaName, identifier)
สมมติว่าคุณมีเอกสาร article ที่จัดเก็บด้วยตัวระบุ "welcome-post":
// จัดเก็บใน CMS เป็น: article / welcome-post
{
"headline": "ยินดีต้อนรับสู่แพลตฟอร์มของเรา",
"author": "Sarah Chen",
"body": "เรายินดีที่จะประกาศ...",
"tags": ["ประกาศ", "ข่าว"]
}
ดึงเอกสารทั้งฉบับ:
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)
// ดึงประเทศทั้งหมด
documents.find("country")
// ดึงประเทศด้วยตัวกรอง
documents.find("country", { "where": { "code": "us" } })
ผลลัพธ์จะเป็นอาร์เรย์ของเอกสารที่ตรงตามเงื่อนไข
CEL รองรับการดึงเนื้อหาเอกสารที่แปลแล้วสองวิธี ได้แก่ การแปลอัตโนมัติตาม locale และการค้นหาคำแปลแบบระบุภาษาโดยตรง
เมื่อกำหนด meta.locale เช่นจากพารามิเตอร์เส้นทางหรือการตั้งค่าของผู้ใช้ documents.get() จะผสานเนื้อหาที่แปลแล้วให้อัตโนมัติ:
// หาก meta.locale เป็น "fr" จะคืนคำแปลภาษาฝรั่งเศสที่ผสานกับเอกสารพื้นฐาน
documents.get("greeting", "welcome").headline
การทำงานคือ:
meta.locale ไม่ใช่ en หรือ en-US จะค้นหาคำแปลในตาราง translations{ ...baseContent, ...translatedContent }ฟิลด์ที่มีคำแปลจะมีลำดับความสำคัญสูงกว่า ส่วนฟิลด์ที่ยังไม่ได้แปลจะใช้ค่าจากเอกสารพื้นฐาน
หากต้องการดึงคำแปลภาษาใดภาษาหนึ่งโดยไม่ขึ้นกับ locale ปัจจุบัน:
ไวยากรณ์: documents.translated(schemaName, identifier, locale)
// ดึงคำแปลภาษาสเปนเสมอ
documents.translated("greeting", "welcome", "es").headline
// ดึงคำแปลตามพารามิเตอร์ URL
documents.translated("product", meta.params.id, meta.params.lang).description
// เปรียบเทียบคำแปล
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
เมื่อเข้าชม /countries/us ค่า meta.params.code คือ "us" และผลลัพธ์คือชื่อประเทศที่ตรงกัน
meta.locale == "ar-SA" ? "ยินดีต้อนรับทุกคน" : "ยินดีต้อนรับ"
documents.get("country", documents.get("article", "us-news").countryCode).name
การทำงานคือดึงบทความ อ่านค่า countryCode จากนั้นดึงเอกสารประเทศและอ่านฟิลด์ name
documents.get("article", meta.params.slug) != null
? documents.get("article", meta.params.slug).headline
: "ไม่พบบทความ"
หรือตรวจสอบฟิลด์เฉพาะ:
documents.get("article", "intro").author != null
? documents.get("article", "intro").author
: "ไม่ทราบผู้เขียน"
"featured" in documents.get("article", "welcome-post").tags
คืนค่า true หากบทความมีแท็กดังกล่าว
documents.get("article", "welcome-post").tags[0]
size(documents.get("article", "welcome-post").tags)
ใช้ [0] เพื่อดึงรายการแรก และใช้ size() เพื่อนับจำนวนรายการ
เส้นทางแบบพารามิเตอร์เป็นหัวใจของการสร้างหน้าแบบไดนามิกและรองรับหลายภาษา เมื่อกำหนดรูปแบบ เช่น /{lang}/landingPage CMS จะดึงค่าพารามิเตอร์จาก 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" } |
พารามิเตอร์แต่ละตัวสามารถผูกกับ schema เพื่อใช้ตรวจสอบความถูกต้องได้:
{
"pattern": "/{lang}/landingPage",
"param_bindings": {
"lang": "language"
}
}
CMS จะดึงส่วน lang จาก URL ตรวจสอบกับ schema language และทำให้เอกสารฉบับเต็มพร้อมใช้งานในพารามิเตอร์ที่แก้ไขแล้ว
documents.get("greeting", meta.params.lang).headline
เมื่อเข้าชม /ko/landingPage ค่า meta.params.lang คือ "ko" และระบบจะดึงเนื้อหาภาษาเกาหลี
สำหรับ /{country}/{lang}/products:
{
"pattern": "/{country}/{lang}/products",
"param_bindings": {
"country": "country",
"lang": "language"
}
}
// ดึงชื่อประเทศ
documents.get("country", meta.params.country).name
// ดึงรายการสินค้าตามประเทศ
documents.find("product", { "where": { "country": meta.params.country } })
// แสดงคำต้อนรับตามประเทศและภาษา
documents.get("greeting", meta.params.lang).headline + " จาก " + documents.get("country", meta.params.country).name
CMS จะตรวจสอบพารามิเตอร์ตามลำดับชั้น โดยตรวจสอบประเทศก่อน จากนั้นภาษา และอาจตรวจสอบเพิ่มเติมว่าภาษานั้นอยู่ในอาร์เรย์ country.languages[] หรือไม่
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 ? "ลึก" : "ตื้น"
"admin" in meta.segments ? "โหมดผู้ดูแลระบบ" : "โหมดสาธารณะ"
has(meta.params.lang) ? meta.params.lang : meta.segments[0]
ออบเจ็กต์ meta มีบริบททั้งหมดของคำขอปัจจุบัน:
| คุณสมบัติ | ชนิด | คำอธิบาย |
|---|---|---|
meta.locale | string | รหัส locale ปัจจุบัน เช่น "en-US", "ko-KR", "ar-SA" |
meta.params | Record<string, string> | พารามิเตอร์เส้นทางที่ดึงจากรูปแบบ URL |
meta.segments | string[] | พาธ URL ที่แยกเป็นส่วน ๆ |
meta.docId | string | null | UUID ของเอกสารปัจจุบัน หรือ null หากเป็นเอกสารใหม่ |
meta.title | string | ชื่อเอกสารปัจจุบัน |
รหัส locale ใช้รูปแบบ BCP 47:
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"
meta.locale.split("-")[0]
พารามิเตอร์เส้นทางเป็นสตริงเสมอ และ CMS จะตรวจสอบกับ schema ที่ผูกไว้ก่อนประเมินผล:
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)
meta.segments[0]
meta.segments[1]
size(meta.segments)
"products" in meta.segments
meta.docId != null ? "กำลังแก้ไข" : "กำลังสร้างใหม่"
meta.docId != null ? documents.get("article", meta.docId).status : "ฉบับร่าง"
"กำลังแก้ไข: " + meta.title
meta.title.contains("Draft") ? "กำลังดำเนินการ" : "เผยแพร่แล้ว"
เมื่อทราบ schema แต่ตัวระบุเป็นแบบไดนามิก สามารถเขียนให้กระชับขึ้นได้:
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("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() จะถูกติดตามเพื่อทำให้แคชไม่ถูกต้องโดยอัตโนมัติเมื่อเอกสารอ้างอิงเปลี่ยนแปลง
ตัวอย่างนี้สร้างหน้า Landing Page หลายภาษาที่เข้าถึงได้ผ่าน /{lang}/landingPage
ในส่วนผู้ดูแลระบบ CMS ให้สร้าง schema แบบกำหนดเองชื่อ greeting พร้อมฟิลด์ code, headline, subheadline, ctaText และ ctaUrl
สร้างเอกสารสำหรับแต่ละภาษา โดยใช้ตัวระบุ เช่น greeting/ko, greeting/en และ greeting/ja
กำหนดค่า:
/{lang}/landingPagelang ไปยังคอมโพเนนต์ languagedocuments.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 จะ resolve หน้า ดึง meta.params จาก URL ประเมิน binding ของ CEL ฝั่งเซิร์ฟเวอร์ และเรนเดอร์แต่ละบล็อกผ่าน registry
| URL | พาดหัวที่คาดหวัง |
|---|---|
/ko/landingPage | 환영 |
/en/landingPage | Welcome |
/ja/landingPage | いらっしゃいませ |
เมื่อผู้ใช้เข้าชม /ko/landingPage CMS จะจับคู่เส้นทาง ดึง meta.params.lang = "ko" ตรวจสอบค่า ประเมิน CEL และส่งบล็อกที่แปลแล้วกลับไปยังไคลเอนต์
interface CelMeta {
/** รหัส locale ปัจจุบัน */
locale: string;
/** พารามิเตอร์เส้นทางที่ดึงจาก URL */
params: Record<string, string>;
/** ส่วนต่าง ๆ ของพาธ URL */
segments: string[];
/** ID เอกสารปัจจุบัน */
docId: string | null;
/** ชื่อเอกสารปัจจุบัน */
title: string;
}
ฟังก์ชัน extractParams ประมวลผลพาธ URL ดังนี้:
รูปแบบ: /{country}/{lang}/products
พาธ: /us/en/products
อัลกอริทึม:
1. ทำให้ทั้งสองรูปแบบเป็นมาตรฐาน โดยลบ slash ท้ายออก
2. แยกเป็นส่วนต่าง ๆ
3. ตรวจสอบว่าจำนวนส่วนเท่ากัน
4. หากส่วนของรูปแบบขึ้นต้นด้วย : หรือ {} ให้ดึงเป็นพารามิเตอร์ มิฉะนั้นต้องตรงกันทุกประการ
5. ส่งคืน: { country: "us", lang: "en" }
{ "lang": "language" }
{
"lang": {
"schemaName": "language",
"slugField": "code"
},
"slug": {
"schemaName": "article",
"slugField": "slug"
}
}
เมื่อดึงข้อมูลผ่าน documents.get(schema, identifier):
idcontent.codecontent.slugtitleจึงสามารถอ้างอิงเอกสารด้วยตัวระบุเฉพาะที่เหมาะสมได้อย่างยืดหยุ่น