profound-logoProfound CMS
⌘K
Admin
Theme
Tài liệuHướng dẫnBlogTriết học
Tài liệuHướng dẫnBlogTriết học

Kết hợp

Định tuyến tham sốCác loại ComponentSetup server sent events (SSE) content refetchThiết lập proxy bảng điều khiển quản trịCEL Scripting in Template BuilderProject ScaffoldingThư viện phương tiện

Không đầu

Bắt đầu nhanhJson và claude codeComponent Zod Pull

REST API

Tổng quan REST APIgetKết nối API trang webgetGET /routesgetLấy tuyếngetGET /blocksgetLấy các khối kèm bộ đệm CELgetGET /blocks/generatedgetGET /componentsgetLấy tên thành phầngetLấy tên lược đồ tập dữ liệugetGET /content-changes (SSE)patchCập nhật tên lược đồ tập dữ liệupostPOST /translationpatchPATCH /translationsgetGET /usagepostPOST /csvpatchPATCH /csv
All Systems Operational
Powered Byprofound-logo
Theme

CEL Scripting in Template Builder

Hướng dẫn thực tế về cách viết các biểu thức CEL trong CMS.

Hướng dẫn thực tế về cách viết các biểu thức CEL trong CMS.


CEL hoạt động như thế nào

CEL (Common Expression Language) là một ngôn ngữ lập trình nhẹ được tích hợp trong CMS của chúng tôi. Ngôn ngữ này cho phép bạn viết các biểu thức động có thể lấy dữ liệu từ tài liệu, đọc tham số URL và tính toán giá trị ngay khi chạy.

Đây là những gì xảy ra khi một tập lệnh CEL chạy:

Tập lệnh của bạn              Bộ máy                         Kết quả
    |                              |                              |
    v                              v                              v
documents.get("article", "intro") --> Lấy từ cơ sở dữ liệu --> { headline: "Welcome", body: "..." }
         .headline                --> Trích xuất trường      --> "Welcome"

Hãy coi CEL như một ngôn ngữ truy vấn chỉ đọc. Nó không thể sửa đổi bất kỳ thứ gì trong cơ sở dữ liệu - nó chỉ đọc dữ liệu và trả về một kết quả đã tính toán. Điều này giúp CEL an toàn khi sử dụng ở bất kỳ đâu trong CMS.


Các khối xây dựng

Mỗi biểu thức CEL đều có quyền truy cập vào ba thành phần:

Đối tượngLà gìVí dụ
documentsLấy bất kỳ tài liệu nào từ CMSdocuments.get("country", "us")
metaThông tin về yêu cầu hiện tại (locale, tham số URL)meta.locale, meta.params.slug
schemaĐịnh nghĩa các trường của tài liệu hiện tạischema.fields

Tham chiếu chính tài liệu với doc

Khi viết biểu thức CEL trong trình chỉnh sửa tài liệu, bạn có thể truy cập các giá trị trường của tài liệu hiện tại bằng đối tượng doc. Điều này cho phép tạo các trường được tính toán và tham chiếu giữa các trường.

// Truy cập trường giá của tài liệu hiện tại
doc.price

// Tính tổng từ các trường của tài liệu hiện tại
doc.price * doc.quantity

// Điều kiện dựa trên trạng thái của tài liệu hiện tại
doc.status == "published" ? doc.title : "Draft: " + doc.title

Đối tượng doc chứa tất cả giá trị trường của tài liệu đang được chỉnh sửa. Đối tượng này hữu ích cho:

  • Các trường được tính toán (ví dụ: doc.price * doc.quantity)
  • Logic hiển thị có điều kiện dựa trên trạng thái tài liệu
  • Các biểu thức kiểu xác thực

Lấy tài liệu

Tính năng mạnh mẽ nhất của CEL là lấy tài liệu từ bất kỳ đâu trong CMS.

Lấy một tài liệu

Cú pháp: documents.get(schemaName, identifier)

Giả sử bạn có một tài liệu article được lưu với mã định danh "welcome-post":

// Được lưu trong CMS dưới dạng: article / welcome-post
{
  "headline": "Welcome to Our Platform",
  "author": "Sarah Chen",
  "body": "We're excited to announce...",
  "tags": ["announcement", "news"]
}

Để lấy toàn bộ tài liệu:

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

Trả về:

{
  "headline": "Welcome to Our Platform",
  "author": "Sarah Chen",
  "body": "We're excited to announce...",
  "tags": ["announcement", "news"]
}

Để chỉ lấy tiêu đề:

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

Trả về: "Welcome to Our Platform"

Để lấy tác giả:

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

Trả về: "Sarah Chen"


Sử dụng tham số URL

Khi trang của bạn có các route động (như /articles/[slug]), bạn có thể dùng meta.params để lấy tham số URL và lấy đúng tài liệu.

Nếu người dùng truy cập /articles/welcome-post:

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

Trả về: "Welcome to Our Platform"

Đây là cách xây dựng các trang động - cùng một tập lệnh CEL hoạt động cho mọi bài viết, chỉ cần sử dụng slug có trong URL.


Lấy nhiều tài liệu

Cú pháp: documents.find(schemaName) hoặc documents.find(schemaName, filter)

// Lấy tất cả quốc gia
documents.find("country")

Trả về:

[
  { "code": "us", "name": "United States", "flag": "US" },
  { "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
  { "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// Lấy quốc gia với bộ lọc
documents.find("country", { "where": { "code": "us" } })

Trả về:

[
  { "code": "us", "name": "United States", "flag": "US" }
]

Bản dịch

CEL hỗ trợ lấy nội dung tài liệu đã dịch theo hai cách: dịch tự động dựa trên locale và tra cứu bản dịch rõ ràng.

Dịch tự động qua meta.locale

Khi meta.locale được thiết lập (ví dụ từ tham số route hoặc tùy chọn của người dùng), documents.get() sẽ tự động hợp nhất nội dung đã dịch:

// Nếu meta.locale là "fr", trả về bản dịch tiếng Pháp được hợp nhất với tài liệu gốc
documents.get("greeting", "welcome").headline

Cách hoạt động:

  1. Lấy nội dung tài liệu gốc
  2. Nếu meta.locale không phải là "en" hoặc "en-US", tìm bản dịch trong bảng translations
  3. Hợp nhất các trường đã dịch lên nội dung gốc: { ...baseContent, ...translatedContent }

Điều này có nghĩa là các trường đã dịch sẽ ghi đè lên trường gốc, còn các trường chưa dịch sẽ dùng lại tài liệu gốc.

Dịch rõ ràng với documents.translated()

Trong những trường hợp cần lấy một bản dịch cụ thể bất kể locale hiện tại:

Cú pháp: documents.translated(schemaName, identifier, locale)

// Luôn lấy bản dịch tiếng Tây Ban Nha
documents.translated("greeting", "welcome", "es").headline

// Lấy bản dịch dựa trên tham số URL
documents.translated("product", meta.params.id, meta.params.lang).description

// So sánh các bản dịch
documents.translated("article", "intro", "en").title + " / " + documents.translated("article", "intro", "fr").title

Ví dụ về bản dịch

Các tài liệu lời chào cùng bản dịch:

// Tài liệu gốc: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }

// Bản dịch (ngôn ngữ: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }

// Bản dịch (ngôn ngữ: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }

Các tập lệnh CEL:

// Với meta.locale = "fr"
documents.get("greeting", "welcome").headline
// Trả về: "Bienvenue"

// Bản dịch tiếng Tây Ban Nha rõ ràng
documents.translated("greeting", "welcome", "es").headline
// Trả về: "Bienvenido"

// Mẫu dự phòng khi thiếu bản dịch
documents.translated("greeting", "welcome", meta.params.lang) != null
  ? documents.translated("greeting", "welcome", meta.params.lang).headline
  : documents.get("greeting", "welcome").headline

Ví dụ thực tế

Ví dụ 1: Tiêu đề khối Hero từ một tài liệu khác

Bạn có một hero-block cần hiển thị tiêu đề lấy từ tài liệu article.

Tài liệu bài viết của bạn (mã định danh: "homepage-hero"):

{
  "headline": "Build Faster, Ship Smarter",
  "subheadline": "The modern CMS for developers"
}

Tập lệnh CEL trong trường tiêu đề của khối hero:

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

Kết quả: Hero hiển thị "Build Faster, Ship Smarter"


Ví dụ 2: Tên quốc gia từ mã

Bạn đang xây dựng trang tại /countries/[code] và muốn hiển thị tên quốc gia đầy đủ.

Các tài liệu quốc gia của bạn:

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

// country / sa
{ "code": "sa", "name": "Saudi Arabia", "flag": "SA", "languages": ["ar", "en"] }

Tập lệnh CEL:

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

Khi người dùng truy cập /countries/us:

  • meta.params.code = "us"
  • Kết quả: "United States"

Khi người dùng truy cập /countries/sa:

  • meta.params.code = "sa"
  • Kết quả: "Saudi Arabia"

Ví dụ 3: Nội dung có điều kiện dựa trên locale

Hiển thị các tiêu đề khác nhau dựa trên locale của người dùng.

meta.locale == "ar-SA" ? "Welcome, everyone" : "Welcome"

Nếu locale là "ar-SA": Trả về "Welcome, everyone" Nếu locale là giá trị khác: Trả về "Welcome"


Ví dụ 4: Chuỗi tra cứu tài liệu

Tài liệu article có trường countryCode và bạn muốn lấy tên quốc gia đầy đủ.

Tài liệu bài viết:

{ "headline": "News from the US", "countryCode": "us" }

Tập lệnh CEL:

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

Điều gì xảy ra:

  1. documents.get("article", "us-news") trả về { "headline": "News from the US", "countryCode": "us" }
  2. .countryCode trích xuất "us"
  3. documents.get("country", "us") trả về { "code": "us", "name": "United States", ... }
  4. .name trích xuất "United States"

Kết quả: "United States"


Ví dụ 5: Giá trị dự phòng

Nếu tài liệu có thể không tồn tại, bạn có thể cung cấp giá trị dự phòng:

documents.get("article", meta.params.slug) != null
  ? documents.get("article", meta.params.slug).headline
  : "Article Not Found"

Hoặc kiểm tra xem một trường cụ thể có tồn tại không:

documents.get("article", "intro").author != null
  ? documents.get("article", "intro").author
  : "Unknown Author"

Ví dụ 6: Làm việc với danh sách

Bài viết của bạn có các thẻ và bạn muốn kiểm tra xem một thẻ cụ thể có tồn tại không:

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

Trả về: true nếu bài viết có thẻ "featured"

Lấy thẻ đầu tiên:

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

Trả về: "announcement" (thẻ đầu tiên)

Đếm số thẻ:

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

Trả về: 2 (số lượng thẻ)


Route tham số và meta.params

Route tham số là chìa khóa để xây dựng các trang động, bản địa hóa. Khi định nghĩa mẫu route như /{lang}/landingPage, CMS sẽ trích xuất các tham số từ URL và cung cấp chúng qua meta.params.

Cách tham số route hoạt động

Định nghĩa mẫu route: Route sử dụng cú pháp :paramName hoặc {paramName} để định nghĩa các phân đoạn động:

MẫuURL ví dụTham số được trích xuất
/:lang/landingPage/ko/landingPage{ lang: "ko" }
/{country}/{lang}/products/us/en/products{ country: "us", lang: "en" }
/articles/:slug/articles/welcome-post{ slug: "welcome-post" }

Liên kết tham số: Mỗi tham số route có thể được liên kết với một schema tài liệu để xác thực:

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

Liên kết này cho CMS biết:

  1. Trích xuất phân đoạn lang từ URL
  2. Xác thực nó với schema language (tìm tài liệu có content.code khớp)
  3. Nếu hợp lệ, cung cấp toàn bộ tài liệu trong các tham số đã phân giải

Ví dụ: Trang đích dựa trên ngôn ngữ

Cấu hình route:

  • Đường dẫn: /{lang}/landingPage
  • Mẫu: /{lang}/landingPage
  • Liên kết tham số: { "lang": "language" }

Các tài liệu lời chào:

// greeting / ko
{ "code": "ko", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/ko/get-started" }

// greeting / en
{ "code": "en", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Get Started", "ctaUrl": "/en/get-started" }

// greeting / ja
{ "code": "ja", "headline": "Welcome", "subheadline": "Welcome to our platform", "ctaText": "Start", "ctaUrl": "/ja/get-started" }

Tập lệnh CEL để lấy nội dung bản địa hóa:

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

Cách phân giải:

URLmeta.params.langKết quả
/ko/landingPage"ko""Welcome"
/en/landingPage"en""Welcome"
/ja/landingPage"ja""Welcome"

Mẫu nâng cao: Route quốc gia + ngôn ngữ

Đối với các route như /{country}/{lang}/products:

Cấu hình route:

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

Các tập lệnh CEL:

// Lấy tên quốc gia
documents.get("country", meta.params.country).name

// Lấy danh sách sản phẩm dựa trên quốc gia
documents.find("product", { "where": { "country": meta.params.country } })

// Kết hợp: Hiển thị lời chào theo quốc gia bằng ngôn ngữ của người dùng
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name

Chuỗi xác thực: CMS xác thực các tham số theo thứ bậc. Đối với route /{country}/{lang}:

  1. Xác thực tham số country với schema country
  2. Xác thực tham số lang với schema language
  3. Tùy chọn xác thực rằng lang nằm trong mảng country.languages[] (xác thực phân cấp)

meta.segments - Truy cập đường dẫn URL thô

meta.segments cung cấp đường dẫn URL thô dưới dạng một mảng, hữu ích khi cần truy cập theo vị trí mà không có tham số được đặt tên.

Cách hoạt động:

Đường dẫn URLmeta.segments
/articles/tech/ai-news["articles", "tech", "ai-news"]
/ko/landingPage["ko", "landingPage"]
/us/en/products/featured["us", "en", "products", "featured"]
/[]

Khi nào dùng meta.segments thay vì meta.params

Trường hợp sử dụngCách tiếp cận tốt nhất
Tham số được đặt tên từ mẫu routemeta.params.lang
Truy cập theo vị trímeta.segments[0]
Lấy độ sâu đường dẫnsize(meta.segments)
Kiểm tra đường dẫn có chứa một phân đoạn"admin" in meta.segments

Ví dụ với meta.segments

// Lấy phân đoạn đầu tiên (thường là mã ngôn ngữ)
meta.segments[0]

// Kiểm tra độ sâu đường dẫn
size(meta.segments) > 2 ? "deep" : "shallow"

// Kiểm tra xem có đang ở khu vực quản trị không
"admin" in meta.segments ? "admin mode" : "public mode"

// Dự phòng: Dùng phân đoạn nếu tham số chưa được liên kết
has(meta.params.lang) ? meta.params.lang : meta.segments[0]

Tham chiếu đầy đủ về đối tượng meta

Đối tượng meta chứa toàn bộ ngữ cảnh của yêu cầu hiện tại:

Thuộc tínhKiểuMô tả
meta.localestringMã locale hiện tại (ví dụ: "en-US", "ko-KR", "ar-SA")
meta.paramsRecord<string, string>Các tham số route được trích xuất từ mẫu URL
meta.segmentsstring[]Đường dẫn URL được tách thành các phân đoạn
meta.docId`string \null`UUID của tài liệu hiện tại (null đối với tài liệu mới)
meta.titlestringTiêu đề tài liệu hiện tại

meta.locale

Mã locale tuân theo định dạng BCP 47 (ngôn ngữ-khu vực):

// Kiểm tra locale cho các ngôn ngữ RTL
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"

// Chỉ lấy phần ngôn ngữ
meta.locale.split("-")[0]  // Không được hỗ trợ - hãy dùng meta.params.lang

meta.params

Các tham số route luôn là chuỗi. CMS sẽ xác thực chúng với các schema đã liên kết trước khi đánh giá:

// Truy cập tham số được đặt tên
meta.params.lang           // "ko"
meta.params.country        // "us"
meta.params.slug           // "welcome-post"

// Kiểm tra tham số có tồn tại không
has(meta.params.category)  // true/false

// Dùng trong truy vấn tài liệu
documents.get("greeting", meta.params.lang)
documents.ref("airports").get(meta.params.code)

meta.segments

Các phân đoạn URL thô dưới dạng mảng:

// Truy cập theo chỉ mục (bắt đầu từ 0)
meta.segments[0]           // Phân đoạn đầu tiên
meta.segments[1]           // Phân đoạn thứ hai

// Kiểm tra độ dài
size(meta.segments)        // Số lượng phân đoạn

// Kiểm tra thành phần
"products" in meta.segments  // Đường dẫn có chứa "products" không?

meta.docId

UUID của tài liệu hiện tại, hữu ích cho các tập lệnh tự tham chiếu:

// Chỉ khả dụng khi chỉnh sửa tài liệu hiện có
meta.docId != null ? "editing" : "creating new"

// Dùng trong logic điều kiện
meta.docId != null ? documents.get("article", meta.docId).status : "draft"

meta.title

Tiêu đề của tài liệu hiện tại:

// Dùng để hiển thị
"Editing: " + meta.title

// Điều kiện dựa trên tiêu đề
meta.title.contains("Draft") ? "work in progress" : "published"

documents.ref() - Tra cứu theo chuỗi

Để có cú pháp rõ ràng hơn khi schema đã biết nhưng mã định danh là động:

// Cách truyền thống
documents.get("airports", meta.params.code).name

// Dùng ref() - tách schema khỏi mã định danh động
documents.ref("airports").get(meta.params.code).name

Cả hai cách là tương đương, nhưng ref() làm phần động trở nên rõ ràng hơn.


Tham chiếu nhanh

Lấy tài liệu

documents.get("schema", "identifier")       // Lấy một tài liệu
documents.get("schema", "id").fieldName     // Lấy một trường cụ thể
documents.find("schema")                    // Lấy tất cả tài liệu
documents.find("schema", { "where": {...}}) // Truy vấn có bộ lọc
documents.ref("schema").get(identifier)     // Tra cứu theo chuỗi
documents.translated("schema", "id", "fr")  // Lấy với locale rõ ràng

Biến ngữ cảnh

meta.locale          // "en-US", "ar-SA", v.v.
meta.params.xyz      // Tham số URL có tên "xyz"
meta.segments        // Đường dẫn URL dưới dạng mảng: ["articles", "intro"]
meta.segments[0]     // Phân đoạn đường dẫn đầu tiên
meta.docId           // ID tài liệu hiện tại (hoặc null)
meta.title           // Tiêu đề tài liệu hiện tại
doc.fieldName        // Giá trị trường của tài liệu hiện tại (trong ngữ cảnh trình chỉnh sửa)

Toán tử

// So sánh
==  !=  <  <=  >  >=

// Logic
&&  ||  !

// Toán tử ba ngôi (nếu-thì-không thì)
condition ? valueIfTrue : valueIfFalse

// Kiểm tra thành phần
"value" in listOrMap

Các hàm thường dùng

size(list)                    // Đếm số phần tử
size(string)                  // Độ dài chuỗi
"text".startsWith("te")       // true
"text".endsWith("xt")         // true
"text".contains("ex")         // true
has(object.property)          // Kiểm tra thuộc tính có tồn tại không
hasProperty(obj, "key")       // Kiểm tra đối tượng có khóa không (cú pháp thay thế)

Thông báo lỗi

Nếu xảy ra sự cố, bạn sẽ thấy một trong các lỗi sau:

LỗiÝ nghĩa
SYNTAX_ERRORLỗi chính tả trong tập lệnh (thiếu dấu ngoặc kép, toán tử không hợp lệ)
TYPE_ERRORBạn đang kết hợp các kiểu dữ liệu không tương thích
RUNTIME_ERRORTập lệnh đã chạy nhưng gặp sự cố (biến không xác định)
FETCH_LIMIT_EXCEEDEDBạn đang lấy quá nhiều tài liệu (tối đa 50)
TIMEOUTTập lệnh chạy quá lâu (tối đa 5 giây)
AST_DEPTH_EXCEEDEDBiểu thức lồng quá sâu (độ sâu tối đa: 50)
SCRIPT_TOO_LONGTập lệnh vượt quá giới hạn 5000 ký tự

Khả năng mở rộng và tính năng trong tương lai

Bộ máy CEL được thiết kế để có thể mở rộng. Các khả năng dự kiến trong tương lai bao gồm:

Dự kiến: Tích hợp máy chủ MCP

// Tương lai: Gọi các dịch vụ bên ngoài qua MCP
mcp.translate(meta.params.text, "en", meta.params.lang)
mcp.analyze(documents.get("article", meta.params.id).body)

Dự kiến: Khả năng AI

// Tương lai: Tạo nội dung bằng 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"])

Các khả năng này sẽ được bổ sung thông qua hệ thống hàm đã đăng ký, đồng thời duy trì khả năng tương thích ngược với các tập lệnh hiện có.


Mẹo

  1. Dùng tính năng tự động hoàn thành - Gõ documents. hoặc meta. và trình chỉnh sửa sẽ hiển thị các tùy chọn có sẵn
  2. Bắt đầu đơn giản - Trước tiên hãy kiểm tra với documents.get("schema", "id"), sau đó thêm .fieldName
  3. Kiểm tra null - Nếu tài liệu có thể không tồn tại, hãy thêm giá trị dự phòng bằng != null ? ... : ...
  4. Không lấy thừa dữ liệu - Mỗi documents.get() hoặc documents.find() đều được tính vào giới hạn 50 lần lấy
  5. Ưu tiên meta.params hơn meta.segments - Tham số được đặt tên đã được xác thực và đáng tin cậy hơn
  6. Dùng has() cho tham số tùy chọn - Kiểm tra has(meta.params.category) trước khi truy cập
  7. Dùng documents.ref() cho mã định danh động - Cú pháp rõ ràng hơn khi schema cố định nhưng mã định danh thay đổi
  8. Dùng doc.fieldName cho tham chiếu chính tài liệu - Truy cập các trường của tài liệu hiện tại trong biểu thức tính toán

Tham chiếu giữa các tài liệu

Phần này trình bày các mẫu nâng cao để liên kết các tài liệu với nhau và xây dựng cấu trúc nội dung quan hệ.

Mẫu tham chiếu cơ bản

Dạng đơn giản nhất: một tài liệu tham chiếu đến tài liệu khác bằng mã định danh.

// Bài viết lưu ID tác giả, lấy tên tác giả
documents.get("author", documents.get("article", "intro").authorId).name

Tra cứu theo chuỗi với documents.ref()

Để có cú pháp rõ ràng hơn khi mã định danh là động:

// Cách truyền thống
documents.get("country", documents.get("airport", meta.params.code).countryCode).name

// Dùng ref() - rõ ràng hơn khi schema đã biết nhưng mã định danh là động
documents.ref("country").get(documents.get("airport", meta.params.code).countryCode).name

Chuỗi tham chiếu nhiều cấp

Xây dựng các quan hệ sâu bằng cách nối nhiều lần tra cứu:

// Sân bay → Quốc gia → Khu vực → Châu lục
documents.get("continent",
  documents.get("region",
    documents.get("country",
      documents.get("airport", meta.params.code).countryCode
    ).regionCode
  ).continentCode
).name

Tham chiếu kèm bản dịch

Kết hợp tham chiếu tài liệu với bản dịch:

// Lấy tên quốc gia đã bản địa hóa cho một sân bay
documents.translated("country",
  documents.get("airport", meta.params.code).countryCode,
  meta.params.lang
).name

Các mẫu tham chiếu theo trường hợp sử dụng

Mẫu 1: Tra cứu khóa ngoại

Tài liệu lưu ID tham chiếu đến một tài liệu khác.

// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// Phân giải tên tác giả
documents.get("author", documents.get("article", meta.params.slug).authorId).name

// Phân giải danh mục với giá trị dự phòng
documents.get("article", meta.params.slug).categoryId != null
  ? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
  : "Uncategorized"

Mẫu 2: Tham chiếu dựa trên mã

Các tài liệu tham chiếu lẫn nhau bằng mã ngữ nghĩa thay vì UUID.

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

// country / us
{ "code": "us", "name": "United States", "currencyCode": "usd" }

// currency / usd
{ "code": "usd", "symbol": "$", "name": "US Dollar" }
// Chuỗi Sân bay → Quốc gia → Tiền tệ
documents.get("currency",
  documents.get("country",
    documents.get("airport", meta.params.code).countryCode
  ).currencyCode
).symbol
// Với JFK: Trả về "$"

Mẫu 3: Tự tham chiếu với ngữ cảnh doc

Dùng doc cho các trường được tính toán tham chiếu đến tài liệu khác dựa trên giá trị của tài liệu hiện tại.

// Trong tài liệu sản phẩm, lấy thông tin chi tiết danh mục liên quan
documents.get("category", doc.categoryId).description

// Chi phí vận chuyển được tính dựa trên quốc gia xuất xứ của sản phẩm
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight

Mẫu 4: Tham chiếu hai chiều

Khi các tài liệu tham chiếu lẫn nhau, hãy cẩn thận với giới hạn lấy dữ liệu.

// Lấy tác giả của bài viết, sau đó lấy các bài viết khác của tác giả (hãy chú ý số lần lấy!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })

Mẫu 5: Tham chiếu đa hình

Khi một trường có thể tham chiếu đến các schema khác nhau:

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

// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// Tra cứu schema động dựa trên 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

Theo dõi phụ thuộc

Mọi lời gọi documents.get(), documents.find() và documents.ref().get() đều được theo dõi để vô hiệu hóa bộ nhớ đệm. Khi một tài liệu được tham chiếu thay đổi, CMS biết những biểu thức CEL nào cần được đánh giá lại.

Các phụ thuộc được theo dõi gồm:

  • get: schema:identifier - Phụ thuộc vào một tài liệu cụ thể
  • ref: schema:identifier - Giống get, thông qua cú pháp chuỗi
  • query: schema:* - Phụ thuộc cấp schema (bất kỳ tài liệu nào trong schema)

Thực hành tốt nhất khi tham chiếu

  1. Giảm độ sâu chuỗi - Mỗi cấp làm tăng độ trễ và số lần lấy dữ liệu
  2. Lưu kết quả trung gian vào bộ nhớ đệm - Nếu cần cùng một giá trị lồng nhau hai lần, hãy lấy tài liệu cha một lần
  3. Dùng kiểm tra null - Tham chiếu có thể hỏng nếu tài liệu bị xóa
  4. Ưu tiên mã thay cho UUID - Mã dễ đọc trong biểu thức và ổn định giữa các môi trường
  5. Theo dõi giới hạn lấy dữ liệu - Chuỗi tham chiếu phức tạp có thể nhanh chóng chạm giới hạn 50 lần lấy
// Không tốt: Lấy cùng một tài liệu hai lần
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio

// Tốt hơn: Dùng điều kiện để kiểm tra một lần
documents.get("article", "intro").authorId != null
  ? documents.get("author", documents.get("article", "intro").authorId).name
  : "Unknown Author"

Phụ lục A: Ví dụ đầy đủ về route tham số

Phần hướng dẫn này tạo một trang đích đa ngôn ngữ có thể truy cập tại /{lang}/landingPage.

Bước 1: Tạo schema tài liệu Greeting

Trong trang quản trị CMS, tạo schema tùy chỉnh có tên 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" }
  ]
}

Bước 2: Tạo các tài liệu Greeting

Tạo tài liệu cho từng ngôn ngữ:

Tài liệu: greeting/ko

{
  "code": "ko",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Get Started",
  "ctaUrl": "/ko/get-started"
}

Tài liệu: greeting/en

{
  "code": "en",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Get Started",
  "ctaUrl": "/en/get-started"
}

Tài liệu: greeting/ja

{
  "code": "ja",
  "headline": "Welcome",
  "subheadline": "Welcome to our platform",
  "ctaText": "Start",
  "ctaUrl": "/ja/get-started"
}

Bước 3: Tạo trang

Tạo một trang với cấu hình sau:

  • Đường dẫn/Mẫu: /{lang}/landingPage
  • Trạng thái: Đang hoạt động
  • Ánh xạ phân đoạn động: ánh xạ lang → thành phần language
  {
    "lang": "language"
  }

Bước 4: Thêm các khối với tập lệnh CEL

Thêm một khối hero vào route với các tập lệnh CEL sau cho từng trường:

Trường Headline:

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

Trường Subheadline:

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

Trường CTA Text:

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

Trường CTA URL:

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

Bước 5: Sử dụng trong Next.js

Thêm một route bắt tất cả. ParametricRoutePage phân giải trang, trích xuất meta.params từ URL, đánh giá các liên kết CEL ở phía máy chủ và hiển thị từng khối thông qua registry của bạn — bạn không cần tự xây dựng ngữ cảnh meta hoặc gọi client cấp thấp.

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

Bước 6: Kiểm tra các route

Truy cập các URL sau để xem nội dung bản địa hóa:

URLTiêu đề dự kiến
/ko/landingPage환영
/en/landingPageWelcome
/ja/landingPageいらっしゃいませ

Cách phân giải hoạt động

Khi người dùng truy cập /ko/landingPage:

  1. Khớp route: CMS khớp với mẫu /{lang}/landingPage
  2. Trích xuất tham số: meta.params.lang = "ko"
  3. Xác thực: CMS xác thực rằng "ko" tồn tại trong schema language
  4. Đánh giá CEL: Các tập lệnh như documents.get("greeting", meta.params.lang) được phân giải thành nội dung tiếng Hàn
  5. Phản hồi: Các khối đã bản địa hóa được trả về client

Phụ lục B: Tài liệu kỹ thuật tham khảo

Giao diện CelMeta (TypeScript)

interface CelMeta {
  /** Mã locale hiện tại (ví dụ: 'en-US') */
  locale: string;
  /** Các tham số route được trích xuất từ URL */
  params: Record<string, string>;
  /** Các phân đoạn đường dẫn URL */
  segments: string[];
  /** ID tài liệu hiện tại (nếu đang chỉnh sửa tài liệu hiện có) */
  docId: string | null;
  /** Tiêu đề tài liệu hiện tại */
  title: string;
}

Thuật toán trích xuất tham số

Hàm extractParams xử lý các đường dẫn URL:

Mẫu:      /{country}/{lang}/products
Đường dẫn: /us/en/products

Thuật toán:
1. Chuẩn hóa cả hai (xóa dấu gạch chéo ở cuối)
2. Tách thành các phân đoạn: ["us", "en", "products"] và ["{country}", "{lang}", "products"]
3. So khớp số lượng phân đoạn (phải bằng nhau)
4. Với mỗi cặp phân đoạn:
   - Nếu mẫu bắt đầu bằng : hoặc {}, trích xuất thành tham số
   - Nếu không, phải khớp chính xác
5. Trả về: { country: "us", lang: "en" }

Các định dạng liên kết tham số được hỗ trợ

// Liên kết đơn giản (dùng trường "code" để tra cứu)
{ "lang": "language" }

// Liên kết chi tiết (trường slug tùy chỉnh)
{
  "lang": {
    "schemaName": "language",
    "slugField": "code"
  },
  "slug": {
    "schemaName": "article",
    "slugField": "slug"
  }
}

Thứ tự ưu tiên khi tra cứu tài liệu

Khi lấy dữ liệu qua documents.get(schema, identifier):

  1. Khớp UUID: Nếu mã định danh là UUID hợp lệ, lấy theo id
  2. Trường code: Kiểm tra trường content.code
  3. Trường slug: Kiểm tra trường content.slug
  4. Khớp tiêu đề: Kiểm tra trường title

Điều này cho phép tham chiếu tài liệu linh hoạt bằng bất kỳ mã định danh duy nhất nào.

Continue Reading
Previous‹Thiết lập proxy bảng điều khiển quản trịNextProject Scaffolding›