profound-logoProfound CMS
⌘K
Admin
Theme
Docsبرنامج تعليميBlogPhilosophy
Docsبرنامج تعليميBlogPhilosophy

Hybrid

التوجيه البارامتريTypes of Componentsإس إس إيInstall Profound CMS as a proxyالبرمجة النصية في منشئ القوالبProject Scaffoldingمكتبة الوسائط

بلا رأس

البدء السريعJSON و Claude CodeComponent Zod Pull

واجهة برمجة تطبيقات REST

نظرة عامة على واجهة RESTgetربط موقع الويب بواجهة برمجة التطبيقاتgetالحصول على المساراتgetالحصول على مسارgetالحصول على الكتلgetجلب الكتل مع ذاكرة CEL المؤقتةgetGET /blocks/generatedgetالحصول على المكوناتgetGET /components/{name}getالحصول على اسم مخطط مجموعة البياناتgetالحصول على تغييرات المحتوى (SSE)patchPATCH /dataset/{schema_name}postترجمة المنشورpatchPATCH /translationsgetالحصول على الاستخدامpostPOST /csvpatchPATCH /csv
All Systems Operational
Powered Byprofound-logo
Theme

البرمجة النصية في منشئ القوالب

دليل عملي لكتابة تعبيرات CEL في نظام إدارة المحتوى.

دليل عملي لكتابة تعبيرات CEL في نظام إدارة المحتوى.


كيف تعمل CEL

CEL (لغة التعبيرات العامة) هي لغة برمجة نصية خفيفة مدمجة في نظام إدارة المحتوى لدينا. تتيح لك كتابة تعبيرات ديناميكية يمكنها جلب البيانات من المستندات، وقراءة معلمات عناوين URL، وحساب القيم أثناء التشغيل.

إليك ما يحدث عند تشغيل نص CEL:

نصك البرمجي                  المحرك                         النتيجة
    |                          |                              |
    v                          v                              v
documents.get("article", "intro") --> يجلب من قاعدة البيانات --> { headline: "Welcome", body: "..." }
         .headline                --> يستخرج الحقل          --> "Welcome"

فكّر في CEL كلغة استعلام للقراءة فقط. لا يمكنها تعديل أي شيء في قاعدة البيانات، بل تقرأ البيانات فقط وتُرجع نتيجة محسوبة. وهذا يجعل استخدامها آمنًا في أي مكان داخل نظام إدارة المحتوى.


اللبنات الأساسية

كل تعبير CEL يمكنه الوصول إلى ثلاثة أشياء:

الكائنماهيتهمثال
documentsجلب أي مستند من نظام إدارة المحتوىdocuments.get("country", "us")
metaمعلومات حول الطلب الحالي (الإعدادات المحلية ومعلمات URL)meta.locale, meta.params.slug
schemaتعريفات حقول المستند الحاليschema.fields

الإشارة إلى المستند الحالي باستخدام doc

عند كتابة تعبيرات CEL داخل محرر المستندات، يمكنك الوصول إلى قيم حقول المستند الحالي باستخدام الكائن doc. يتيح ذلك إنشاء الحقول المحسوبة والإشارة إلى الحقول الأخرى.

// الوصول إلى حقل السعر في المستند الحالي
doc.price

// حساب الإجمالي من حقول المستند الحالي
doc.price * doc.quantity

// شرط يعتمد على حالة المستند الحالي
doc.status == "published" ? doc.title : "Draft: " + doc.title

يحتوي الكائن doc على جميع قيم الحقول من المستند الذي يتم تحريره. وهذا مفيد من أجل:

  • الحقول المحسوبة (مثل doc.price * doc.quantity)
  • منطق العرض الشرطي القائم على حالة المستند
  • التعبيرات المشابهة للتحقق من الصحة

جلب المستندات

أقوى ميزة في CEL هي جلب المستندات من أي مكان في نظام إدارة المحتوى.

الحصول على مستند واحد

الصيغة: documents.get(schemaName, identifier)

لنفترض أن لديك مستند article مخزنًا بالمعرّف "welcome-post":

// مخزن في نظام إدارة المحتوى باسم: 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")

يُرجع:

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

لجلب العنوان فقط:

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

يُرجع: "Welcome to Our Platform"

لجلب اسم المؤلف:

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

يُرجع: "Sarah Chen"


استخدام معلمات URL

عندما تحتوي صفحتك على مسارات ديناميكية (مثل /articles/[slug])، يمكنك استخدام meta.params للحصول على معلمة URL وجلب المستند الصحيح.

إذا زار أحدهم /articles/welcome-post:

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

يُرجع: "Welcome to Our Platform"

هكذا تنشئ صفحات ديناميكية؛ إذ يعمل نص CEL نفسه مع أي مقالة، باستخدام قيمة slug الموجودة في عنوان URL.


جلب عدة مستندات

الصيغة: documents.find(schemaName) أو documents.find(schemaName, filter)

// الحصول على جميع الدول
documents.find("country")

يُرجع:

[
  { "code": "us", "name": "United States", "flag": "US" },
  { "code": "sa", "name": "Saudi Arabia", "flag": "SA" },
  { "code": "gb", "name": "United Kingdom", "flag": "GB" }
]
// الحصول على الدول باستخدام عامل تصفية
documents.find("country", { "where": { "code": "us" } })

يُرجع:

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

الترجمات

تدعم CEL جلب محتوى المستندات المترجم بطريقتين: الترجمة التلقائية حسب الإعدادات المحلية، والبحث الصريح عن الترجمة.

الترجمة التلقائية عبر meta.locale

عند تعيين meta.locale (مثلًا من معلمات المسار أو تفضيلات المستخدم)، تدمج documents.get() المحتوى المترجم تلقائيًا:

// إذا كانت قيمة meta.locale هي "fr"، يُرجع الترجمة الفرنسية مدمجة مع المستند الأساسي
documents.get("greeting", "welcome").headline

آلية العمل:

  1. جلب محتوى المستند الأساسي
  2. إذا لم تكن meta.locale هي "en" أو "en-US"، البحث عن الترجمة في جدول translations
  3. دمج الحقول المترجمة فوق المحتوى الأساسي: { ...baseContent, ...translatedContent }

هذا يعني أن الحقول المترجمة تتجاوز الحقول الأساسية، بينما تعود الحقول غير المترجمة إلى المستند الأساسي.

الترجمة الصريحة باستخدام documents.translated()

في الحالات التي تحتاج فيها إلى جلب ترجمة محددة بغض النظر عن الإعداد المحلي الحالي:

الصيغة: 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

مثال على الترجمة

مستندات التحية مع ترجماتها:

// المستند الأساسي: greeting / welcome
{ "headline": "Welcome", "subheadline": "Welcome to our platform" }

// الترجمة (اللغة: "fr")
{ "headline": "Bienvenue", "subheadline": "Bienvenue sur notre plateforme" }

// الترجمة (اللغة: "es")
{ "headline": "Bienvenido", "subheadline": "Bienvenido a nuestra plataforma" }

نصوص CEL:

// مع meta.locale = "fr"
documents.get("greeting", "welcome").headline
// يُرجع: "Bienvenue"

// الترجمة الإسبانية الصريحة
documents.translated("greeting", "welcome", "es").headline
// يُرجع: "Bienvenido"

// نمط بديل للترجمات المفقودة
documents.translated("greeting", "welcome", meta.params.lang) != null
  ? documents.translated("greeting", "welcome", meta.params.lang).headline
  : documents.get("greeting", "welcome").headline

أمثلة واقعية

المثال 1: عنوان كتلة Hero من مستند آخر

لديك hero-block يجب أن يعرض عنوانًا مأخوذًا من مستند article.

مستند المقالة (المعرّف: "homepage-hero"):

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

نص CEL في حقل عنوان كتلة Hero:

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

النتيجة: تعرض كتلة Hero القيمة "Build Faster, Ship Smarter"


المثال 2: اسم الدولة من الرمز

أنت تبني صفحة على /countries/[code] وتريد عرض اسم الدولة كاملًا.

مستندات الدول:

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

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

نص CEL:

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

عند زيارة /countries/us:

  • meta.params.code = "us"
  • النتيجة: "United States"

عند زيارة /countries/sa:

  • meta.params.code = "sa"
  • النتيجة: "Saudi Arabia"

المثال 3: محتوى شرطي بناءً على الإعداد المحلي

اعرض عناوين مختلفة بناءً على الإعداد المحلي للمستخدم.

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

إذا كان الإعداد المحلي "ar-SA": يُرجع "Welcome, everyone" إذا كان الإعداد المحلي أي قيمة أخرى: يُرجع "Welcome"


المثال 4: عمليات البحث المتسلسلة عن المستندات

تحتوي مقالتك على حقل countryCode، وتريد الحصول على اسم الدولة كاملًا.

مستند المقالة:

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

نص CEL:

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

ما يحدث:

  1. تُرجع documents.get("article", "us-news") القيمة { "headline": "News from the US", "countryCode": "us" }
  2. يستخرج .countryCode القيمة "us"
  3. تُرجع documents.get("country", "us") القيمة { "code": "us", "name": "United States", ... }
  4. يستخرج .name القيمة "United States"

النتيجة: "United States"


المثال 5: القيم البديلة

إذا كان من المحتمل ألا يكون المستند موجودًا، يمكنك توفير قيمة بديلة:

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

أو تحقق من وجود حقل محدد:

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

المثال 6: العمل مع القوائم

تحتوي مقالتك على وسوم، وتريد التحقق من وجود وسم محدد:

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

يُرجع: true إذا كانت المقالة تحتوي على وسم "featured"

الحصول على الوسم الأول:

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

يُرجع: "announcement" (الوسم الأول)

حساب عدد الوسوم:

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

يُرجع: 2 (عدد الوسوم)


المسارات المعلمية و meta.params

المسارات المعلمية هي المفتاح لبناء صفحات ديناميكية ومحلية. عند تعريف نمط مسار مثل /{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"
  }
}

يُخبر هذا الربط نظام إدارة المحتوى بما يلي:

  1. استخراج المقطع lang من عنوان URL
  2. التحقق منه مقابل مخطط language (البحث عن مستند يطابق فيه content.code القيمة)
  3. إذا كان صالحًا، إتاحة المستند الكامل ضمن المعلمات المحلولة

مثال: صفحة هبوط قائمة على اللغة

إعداد المسار:

  • المسار: /{lang}/landingPage
  • النمط: /{lang}/landingPage
  • ربط المعلمات: { "lang": "language" }

مستندات التحية:

// 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" }

نص CEL لجلب المحتوى المحلي:

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

كيفية الحل:

URLmeta.params.langالنتيجة
/ko/landingPage"ko""Welcome"
/en/landingPage"en""Welcome"
/ja/landingPage"ja""Welcome"

نمط متقدم: مسارات الدولة واللغة

للمسارات مثل /{country}/{lang}/products:

إعداد المسار:

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

نصوص CEL:

// الحصول على اسم الدولة
documents.get("country", meta.params.country).name

// الحصول على قائمة المنتجات المحلية بناءً على الدولة
documents.find("product", { "where": { "country": meta.params.country } })

// مدمج: عرض تحية خاصة بالدولة بلغة المستخدم
documents.get("greeting", meta.params.lang).headline + " from " + documents.get("country", meta.params.country).name

تسلسل التحقق: يتحقق نظام إدارة المحتوى من المعلمات هرميًا. بالنسبة إلى مسارات /{country}/{lang}:

  1. التحقق من معلمة country مقابل مخطط country
  2. التحقق من معلمة lang مقابل مخطط language
  3. التحقق اختياريًا من أن lang موجودة في مصفوفة country.languages[] (تحقق هرمي)

meta.segments - الوصول إلى مسار URL الخام

يوفر meta.segments مسار URL الخام على هيئة مصفوفة، وهو مفيد عندما تحتاج إلى الوصول الموضعي دون استخدام معلمات مسماة.

آلية العمل:

مسار URLmeta.segments
/articles/tech/ai-news["articles", "tech", "ai-news"]
/ko/landingPage["ko", "landingPage"]
/us/en/products/featured["us", "en", "products", "featured"]
/[]

متى تستخدم meta.segments بدلًا من meta.params

حالة الاستخدامالأسلوب الأفضل
المعلمات المسماة من نمط المسارmeta.params.lang
الوصول حسب الموضعmeta.segments[0]
الحصول على عمق المسارsize(meta.segments)
التحقق من احتواء المسار على مقطع"admin" in meta.segments

أمثلة باستخدام 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

يحتوي الكائن meta على كل السياق المتعلق بالطلب الحالي:

الخاصيةالنوعالوصف
meta.localestringرمز الإعداد المحلي الحالي (مثل "en-US" و"ko-KR" و"ar-SA")
meta.paramsRecord<string, string>معلمات المسار المستخرجة من نمط URL
meta.segmentsstring[]مسار URL مقسم إلى مقاطع
meta.docId`string \null`المعرّف الفريد العام للمستند الحالي (فارغ للمستندات الجديدة)
meta.titlestringعنوان المستند الحالي

meta.locale

يتبع رمز الإعداد المحلي صيغة BCP 47 (اللغة-المنطقة):

// التحقق من الإعداد المحلي للغات التي تُكتب من اليمين إلى اليسار
meta.locale == "ar-SA" || meta.locale == "he-IL" ? "rtl" : "ltr"

// الحصول على جزء اللغة فقط
meta.locale.split("-")[0]  // غير مدعوم - استخدم meta.params.lang بدلًا منه

meta.params

تكون معلمات المسار دائمًا سلاسل نصية. يتحقق نظام إدارة المحتوى منها مقابل المخططات المرتبطة قبل التقييم:

// الوصول إلى معلمة مسماة
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)

meta.segments

مقاطع URL الخام على هيئة مصفوفة:

// الوصول حسب الفهرس (يبدأ من 0)
meta.segments[0]           // المقطع الأول
meta.segments[1]           // المقطع الثاني

// التحقق من الطول
size(meta.segments)        // عدد المقاطع

// التحقق من العضوية
"products" in meta.segments  // هل يحتوي المسار على "products"؟

meta.docId

المعرّف الفريد العام للمستند الحالي، وهو مفيد للنصوص التي تشير إلى نفسها:

// متاح فقط عند تحرير مستندات موجودة
meta.docId != null ? "editing" : "creating new"

// استخدامه في منطق شرطي
meta.docId != null ? documents.get("article", meta.docId).status : "draft"

meta.title

عنوان المستند الحالي:

// استخدامه للعرض
"Editing: " + meta.title

// شرط مبني على العنوان
meta.title.contains("Draft") ? "work in progress" : "published"

documents.ref() - عمليات البحث المتسلسلة

لصياغة أوضح عندما يكون المخطط معروفًا لكن المعرّف ديناميكيًا:

// الأسلوب التقليدي
documents.get("airports", meta.params.code).name

// استخدام ref() - فصل المخطط عن المعرّف الديناميكي
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          // "en-US" و"ar-SA" وغيرهما
meta.params.xyz      // معلمة URL المسماة "xyz"
meta.segments        // مسار URL كمصفوفة: ["articles", "intro"]
meta.segments[0]     // أول مقطع في المسار
meta.docId           // معرّف المستند الحالي (أو null)
meta.title           // عنوان المستند الحالي
doc.fieldName        // قيمة حقل المستند الحالي (في سياق المحرر)

العوامل

// المقارنة
==  !=  <  <=  >  >=

// المنطق
&&  ||  !

// الثلاثي (إذا-وإلا)
condition ? valueIfTrue : valueIfFalse

// العضوية
"value" in listOrMap

الدوال الشائعة

size(list)                    // عد العناصر
size(string)                  // طول السلسلة النصية
"text".startsWith("te")       // true
"text".endsWith("xt")         // true
"text".contains("ex")         // true
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يتجاوز النص البرمجي حد 5000 حرف

قابلية التوسعة والإمكانات المستقبلية

صُمم محرك CEL ليكون قابلًا للتوسعة. تشمل الإمكانات المستقبلية المخطط لها ما يلي:

مخطط لها: التكامل مع خادم MCP

// مستقبلًا: استدعاء خدمات خارجية عبر MCP
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"])

ستُضاف هذه الإمكانات عبر نظام الدوال المسجلة، مع الحفاظ على التوافق العكسي مع النصوص البرمجية الحالية.


نصائح

  1. استخدم الإكمال التلقائي - اكتب documents. أو meta. ليعرض المحرر الخيارات المتاحة
  2. ابدأ ببساطة - اختبر أولًا باستخدام documents.get("schema", "id")، ثم أضف .fieldName
  3. تحقق من null - إذا كان من المحتمل ألا يكون المستند موجودًا، أضف قيمة بديلة باستخدام != null ? ... : ...
  4. لا تجلب بيانات زائدة - يُحتسب كل استدعاء لـ documents.get() أو documents.find() ضمن حد الجلب البالغ 50
  5. فضّل meta.params على meta.segments - فالمعلمات المسماة خاضعة للتحقق وأكثر موثوقية
  6. استخدم has() للمعلمات الاختيارية - تحقق من has(meta.params.category) قبل الوصول إليها
  7. استخدم documents.ref() للمعرّفات الديناميكية - صياغة أوضح عندما يكون المخطط ثابتًا لكن المعرّف ديناميكيًا
  8. استخدم doc.fieldName للإشارات الذاتية - للوصول إلى حقول المستند الحالي داخل التعبيرات المحسوبة

المراجع بين المستندات

يغطي هذا القسم الأنماط المتقدمة لربط المستندات معًا وبناء هياكل محتوى علائقية.

نمط المرجع الأساسي

أبسط صورة: يشير مستند إلى مستند آخر باستخدام المعرّف.

// يخزن المقال معرّف المؤلف، ثم يجلب اسم المؤلف
documents.get("author", documents.get("article", "intro").authorId).name

عمليات البحث المتسلسلة باستخدام documents.ref()

لصياغة أوضح عندما يكون المعرّف ديناميكيًا:

// الأسلوب التقليدي
documents.get("country", documents.get("airport", meta.params.code).countryCode).name

// استخدام ref() - أوضح عندما يكون المخطط معروفًا لكن المعرّف ديناميكيًا
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

أنماط المراجع حسب حالة الاستخدام

النمط 1: البحث باستخدام مفتاح أجنبي

يخزن المستند معرّفًا يشير إلى مستند آخر.

// article / tech-news
{ "title": "Tech Update", "authorId": "author-123", "categoryId": "cat-tech" }
// حل اسم المؤلف
documents.get("author", documents.get("article", meta.params.slug).authorId).name

// حل الفئة مع قيمة بديلة
documents.get("article", meta.params.slug).categoryId != null
  ? documents.get("category", documents.get("article", meta.params.slug).categoryId).name
  : "Uncategorized"

النمط 2: المراجع القائمة على الرموز

تشير المستندات إلى بعضها باستخدام رموز دلالية بدلًا من المعرّفات الفريدة العامة.

// 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" }
// سلسلة المطار → الدولة → العملة
documents.get("currency",
  documents.get("country",
    documents.get("airport", meta.params.code).countryCode
  ).currencyCode
).symbol
// بالنسبة إلى JFK: تُرجع "$"

النمط 3: الإشارة الذاتية مع سياق doc

استخدم doc للحقول المحسوبة التي تشير إلى مستندات أخرى بناءً على قيم المستند الحالي.

// في مستند منتج، جلب تفاصيل الفئة المرتبطة
documents.get("category", doc.categoryId).description

// تكلفة الشحن المحسوبة بناءً على بلد منشأ المنتج
documents.get("shipping-rates", doc.originCountry).baseRate * doc.weight

النمط 4: المراجع ثنائية الاتجاه

عندما تشير المستندات إلى بعضها، انتبه إلى حدود الجلب.

// جلب مؤلف المقالة، ثم جلب مقالات المؤلف الأخرى (راقب عدد عمليات الجلب!)
documents.find("article", { "where": { "authorId": documents.get("article", meta.params.slug).authorId } })

النمط 5: المراجع متعددة الأشكال

عندما يمكن لحقل أن يشير إلى مخططات مختلفة:

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

// content-block / hero-2
{ "type": "hero", "sourceType": "product", "sourceId": "featured-item" }
// البحث في المخطط ديناميكيًا بناءً على 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

تتبع التبعيات

يُتتبع كل استدعاء لـ documents.get() وdocuments.find() وdocuments.ref().get() بغرض إبطال ذاكرة التخزين المؤقت. وعندما يتغير مستند مشار إليه، يعرف نظام إدارة المحتوى تعبيرات CEL التي تحتاج إلى إعادة التقييم.

تشمل التبعيات المتتبعة:

  • get: schema:identifier - تبعية لمستند محدد
  • ref: schema:identifier - مثل get، عبر الصياغة المتسلسلة
  • query: schema:* - تبعية على مستوى المخطط (أي مستند في المخطط)

أفضل الممارسات للمراجع

  1. قلّل عمق السلسلة - يضيف كل مستوى زمن استجابة وعمليات جلب
  2. خزّن النتائج الوسيطة مؤقتًا - إذا احتجت إلى القيمة المتداخلة نفسها مرتين، فاجلب الأصل مرة واحدة
  3. استخدم فحوصات null - قد تتعطل المراجع إذا حُذفت المستندات
  4. فضّل الرموز على المعرّفات الفريدة العامة - الرموز مقروءة في التعبيرات وثابتة عبر البيئات
  5. راقب حدود الجلب - قد تصل سلاسل المراجع المعقدة إلى حد الجلب البالغ 50 بسرعة
// سيئ: يجلب المستند نفسه مرتين
documents.get("author", documents.get("article", "intro").authorId).name + " - " +
documents.get("author", documents.get("article", "intro").authorId).bio

// أفضل: استخدم شرطًا للتحقق مرة واحدة
documents.get("article", "intro").authorId != null
  ? documents.get("author", documents.get("article", "intro").authorId).name
  : "Unknown Author"

الملحق أ: مثال كامل لمسار معلمي

تُنشئ هذه الجولة الإرشادية صفحة هبوط متعددة اللغات يمكن الوصول إليها عبر /{lang}/landingPage.

الخطوة 1: إنشاء مخطط مستند التحية

في لوحة إدارة نظام إدارة المحتوى، أنشئ مخططًا مخصصًا باسم 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" }
  ]
}

الخطوة 2: إنشاء مستندات التحية

أنشئ مستندًا لكل لغة:

المستند: 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"
}

الخطوة 3: إنشاء الصفحة

أنشئ صفحة بالإعدادات التالية:

  • المسار/النمط: /{lang}/landingPage
  • الحالة: منشور
  • تعيينات المقاطع الديناميكية: اربط lang بمكوّن language
  {
    "lang": "language"
  }

الخطوة 4: إضافة الكتل مع نصوص CEL

أضف كتلة Hero إلى المسار مع نصوص CEL التالية لكل حقل:

حقل العنوان:

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

حقل العنوان الفرعي:

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

حقل نص زر CTA:

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

حقل عنوان URL لزر CTA:

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

الخطوة 5: الاستخدام في Next.js

أضف مسارًا شاملًا. يحل ParametricRoutePage الصفحة، ويستخرج meta.params من عنوان URL، ويقيّم روابط CEL على جانب الخادم، ويعرض كل كتلة عبر السجل الخاص بك؛ ولا تحتاج إلى إنشاء سياق meta أو استدعاء العميل منخفض المستوى بنفسك.

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

الخطوة 6: اختبار المسارات

زر عناوين URL التالية لرؤية المحتوى المحلي:

URLالعنوان المتوقع
/ko/landingPage환영
/en/landingPageWelcome
/ja/landingPageいらっしゃいませ

كيفية عمل الحل

عندما يزور المستخدم /ko/landingPage:

  1. مطابقة المسار: يطابق نظام إدارة المحتوى نمط /{lang}/landingPage
  2. استخراج المعلمة: meta.params.lang = "ko"
  3. التحقق: يتحقق نظام إدارة المحتوى من وجود "ko" في مخطط language
  4. تقييم CEL: تُحل نصوص مثل documents.get("greeting", meta.params.lang) إلى المحتوى الكوري
  5. الاستجابة: تُعاد الكتل المحلية إلى العميل

الملحق ب: مرجع تقني

واجهة CelMeta (TypeScript)

interface CelMeta {
  /** رمز الإعداد المحلي الحالي (مثل 'en-US') */
  locale: string;
  /** معلمات المسار المستخرجة من URL */
  params: Record<string, string>;
  /** مقاطع مسار URL */
  segments: string[];
  /** معرّف المستند الحالي (إذا كان مستندًا موجودًا قيد التحرير) */
  docId: string | null;
  /** عنوان المستند الحالي */
  title: string;
}

خوارزمية استخراج المعلمات

تعالج الدالة extractParams مسارات URL:

النمط: /{country}/{lang}/products
المسار: /us/en/products

الخوارزمية:
1. توحيد كليهما (إزالة الشرطات المائلة اللاحقة)
2. تقسيمهما إلى مقاطع: ["us", "en", "products"] و["{country}", "{lang}", "products"]
3. مطابقة عدد المقاطع (يجب أن يكون متساويًا)
4. لكل زوج من المقاطع:
   - إذا بدأ النمط بـ : أو {}، استخرجه كمعلمة
   - وإلا، يجب أن تتطابق القيمتان تمامًا
5. الإرجاع: { country: "us", lang: "en" }

تنسيقات ربط المعلمات المدعومة

// ربط بسيط (يستخدم حقل "code" للبحث)
{ "lang": "language" }

// ربط تفصيلي (حقل slug مخصص)
{
  "lang": {
    "schemaName": "language",
    "slugField": "code"
  },
  "slug": {
    "schemaName": "article",
    "slugField": "slug"
  }
}

أولوية البحث عن المستندات

عند الجلب عبر documents.get(schema, identifier):

  1. مطابقة UUID: إذا كان المعرّف UUID صالحًا، فجلبه باستخدام id
  2. حقل Code: التحقق من حقل content.code
  3. حقل Slug: التحقق من حقل content.slug
  4. مطابقة العنوان: التحقق من حقل title

يتيح ذلك مراجع مرنة للمستندات باستخدام أي معرّف فريد.

Continue Reading
Previous‹Install Profound CMS as a proxyNextProject Scaffolding›