تعمّق في البنية المعمارية
تتتبّع هذه الصفحة مسار طلب من البداية إلى النهاية وتُسمّي الأصناف والأحداث والخدمات الحقيقية المُشارِكة فيه. وهي مكمّلة لصفحة المفاهيم: اقرأ تلك أولًا لاكتساب المصطلحات، ثم اقرأ هذه لفهم الآليات. كما أنها الصفحة التي ينبغي قراءتها إذا كنت (أنت أو مساعد ذكاء اصطناعي) بحاجة إلى نموذج ذهني كامل عن كيفية عمل Melis.
يحتفظ الإصدار v6 من Melis بنفس إطار العمل والوحدات ودورة حياة الطلب الموجودة في v5 — التغيير طرأ على واجهة المكتب الخلفي. لا تزال الأدوات الكلاسيكية المُصيَّرة على الخادم عند /melis موجودة، لكن التجربة الافتراضية أصبحت الآن تطبيق صفحة واحدة (SPA) بلغة React عند /melis-react يُحمّل أدوات React الأصيلة ("bricks") ويرتدّ إلى الأدوات الكلاسيكية داخل iframe. تحافظ الأقسام أدناه على جميع الآليات دون تغيير وتضيف غلاف React في موضعه المناسب.
الإقلاع (Bootstrap)
يُحمّل public/index.php مُحمّل التحميل التلقائي الخاص بـ Composer، ويدمج config/application.config.php مع config/development.config.php (عند وجوده)، ثم يُشغّل تطبيق Laminas MVC.
يبني config/application.config.php قائمة الوحدات ديناميكيًا:
'modules' => array_merge(
MelisCore\MelisModuleManager::getModuleComponents(), // framework components first
MelisCore\MelisModuleManager::getModules() // then Melis modules
),
'module_listener_options' => [
'module_paths' => ['./module', './module/MelisSites'],
'config_glob_paths' => [
realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php',
realpath(__DIR__) . '/autoload/platforms/' . getenv('MELIS_PLATFORM') . '.php',
],
],يجمّع MelisCore\MelisModuleManager (vendor/melisplatform/melis-core/src/MelisModuleManager.php) ثلاثة أنواع من الوحدات بحسب الطلب:
- المكوّنات (Components) — تبعيات إطار العمل، مُعلَنة لكل وحدة في
config/module.load.php. - الوحدات (Modules) — وحدات المكتب الخلفي من
config/melis.module.load.php. - وحدات الموقع (Site modules) — لعنوان URL في المكتب الأمامي، الموقع المُختار بواسطة
MELIS_MODULE(منmodule/MelisSites/<name>أو موقع من مزوّد مثلMelisDemoCms).
يضيف المكتب الخلفي بلغة React وحدتَي بنية تحتية إلى هذه القائمة — melis-react-api (العمود الفقري لواجهة JSON) و melis-react-override (مسار الـ SPA + آلية iframe للأداة القديمة). تُحمَّل كلتاهما عبر module_paths في application.config.php (فهي ليست محمّلة تلقائيًا عبر composer) وتسجّلان نفسيهما عبر StandardAutoloader.
أخيرًا، يحقن ملف المنصّة config/autoload/platforms/<MELIS_PLATFORM>.php اتصال قاعدة البيانات وإعدادات المنصّة في الإعدادات المدمجة.
دورة حياة طلب المكتب الخلفي
يوجد الآن مدخلان إلى المكتب الخلفي، وكلاهما مدفوع بتوجيه MelisCore والتحقق من الهوية:
/melis-react…— غلاف React (الواجهة الافتراضية). يخدم مسار قائم على تعبير نمطي (regex) مستند HTML واحدًا؛ وكل شيء آخر عبارة عن JSON يُجلَب عبر/melis/react-api/…وأدوات تُصيَّر كـ bricks أو iframes (انظر أدناه)./melis…— المكتب الخلفي الكلاسيكي المُصيَّر على الخادم، لا يزال يعمل بكامل وظائفه ويُستخدَم هدفًا لـ iframe الخاص بالأدوات القديمة.
بالنسبة لعنوان URL من نوع /melis…، يقود MelisCore التدفق الكلاسيكي. تُربَط الخطّافات (hooks) الرئيسية في MelisCore\Module::onBootstrap():
- التوجيه (Routing) — يتطابق مسار
melis-backoffice(وأبناؤه:login،authenticate،logout،zoneview،react-tool-page، …). MvcEvent::EVENT_ROUTE← التحقق من الهوية — يُشغَّلModule::checkIdentity(). إذا لم يكن المسار المتطابق ضمن القائمة المستثناة (login،authenticate،change-language، الـ SPA بلغة React ونقاط إقلاعها…) ولم يكن المستخدم موثّقًا، فإنه يُعيد التوجيه إلى/melis/login(أو يُرجِع 404 للطلبات غير GET).- الجلسة واللغة — تُهيّأ حاوية الجلسة
meliscore؛ وتقود اللغة المحلية (melis-lang-locale) الدالةModule::createTranslations()التي تُحمّلlanguage/<locale>.{interface,forms,…}.php. EVENT_DISPATCH— يُضبَط المخطّط (layout) علىlayout/layoutCore، وتعمل المستمعات الأساسية:MelisCoreCheckUserRightsListener(يُعيد قراءة الصلاحيات دوريًا)،MelisCoreFlashMessengerListener،MelisCorePhpWarningListener، وغيرها.- تصيير المناطق (Zone rendering) — واجهة المكتب الخلفي عبارة عن شجرة من المناطق (zones)؛ يحلّ
PluginViewControllerقيمةforwardلكل منطقة (module/controller/action) ويُصيّرها، مجمّعًا الـ HTML النهائي (انظر المفاهيم ← المناطق والتحويلات).
GET /melis
→ route: melis-backoffice
→ EVENT_ROUTE: checkIdentity() → redirect to /melis/login if not logged in
→ EVENT_DISPATCH: layout = layout/layoutCore; rights/flash/warning listeners
→ PluginViewController renders zones (header, left menu, center, footer) via forwards
→ responseدورة حياة المكتب الخلفي بلغة React
بالنسبة لعنوان URL من نوع /melis-react…، يُقسَّم التدفق بين تحميل غلاف يحدث مرة واحدة واستدعاءات JSON لاحقة:
- مسار الـ SPA — يخدم
MelisReactOverride\Controller\SpaControllerغلاف Reactindex.html(المبنيّ داخلmelis-core/public/ui-react/) لـ/melis-reactوكل رابط عميق تحته (/melis-react/news/5، …). المسار عام (public) — إذ يُشغّل تطبيق React شاشة تسجيل الدخول الخاصة به — ويتغلّب على المسار الشامل (catch-all) الخاص بـ MelisFront عبر مسار regex عالي الأولوية. - جلبات الإقلاع (Boot fetches) — يستدعي الغلاف نقاط النهاية العامة لـ
melis-react-api:GET /me(المستخدم الحالي + قدراته)،GET /menu(شجرة التنقّل المُرشَّحة بالصلاحيات)،GET /langs(لغات المكتب الخلفي)،GET /assets(CSS/JS لأدوات iframe)، وGET /react-modules+/bricks-bundle.js(اكتشاف الـ bricks). كل استجابة تلتزم بعقد{ success, data, error? }. - تصيير الأداة — يؤدّي النقر على مدخل قائمة إلى فتح brick (أداة React أصيلة) إذا كانت وحدته تشحن واحدة، وإلا فتح أداة قديمة داخل iframe يخدمها
/melis/react-tool-page?key=<melisKey>(انظر الـ Bricks وآلية iframe). - طبقة مساعد الذكاء الاصطناعي (AI Assistant overlay) — زرّ دردشة عائم يُصيَّر مرة واحدة عند جذر الغلاف (من
melis-ai) يبقى عبر التنقّل ويمكنه قيادة المكتب الخلفي (فتح أداة، فتح صفحة) انطلاقًا من المحادثة. انظر دليل الذكاء الاصطناعي.
GET /melis-react
→ SpaController serves ui-react/index.html (public route)
→ shell boot: GET /me, /menu, /langs, /assets, /react-modules (+ /bricks-bundle.js)
→ click a tool → React brick, or iframe → /melis/react-tool-page?key=<melisKey>
→ AI Assistant overlay mounted at the shell rootالـ Bricks وآلية iframe
غلاف React معياريّ (modular): تظهر الأداة إذا وفقط إذا كانت وحدتها مُفعّلة.
- اكتشاف الـ Bricks — يفحص
GET /melis/react-api/react-modulesالوحدات المُفعّلة بحثًا عنpublic/ui-react/brick.manifest.jsonويُرجِع تعريفاتهاBrickDef({ id, module, route, label, forwardKey, melisKey, subTabs, … }) إضافةً إلى ملف/bricks-bundle.jsواحد مُدمَج. كل brick هو دالة IIFE تسجّل نفسها علىwindow.__MELIS_BRICK_COMPONENTS__؛ وتوقيع?v=<sig>يجعل الحزمة قابلة للتخزين المؤقت بأمان لمدة عام. - مبدّل الجديد/القديم (New / Old toggle) — تحمل معظم الـ bricks مبدّلًا جديد (React) / قديم (iframe). الجديد هو شاشة React الأصيلة؛ والقديم يُحمّل الأداة الكلاسيكية عبر آلية iframe أدناه، بحيث لا يُفقَد شيء أبدًا أثناء الترحيل.
- iframe القديمة — تُصيّر الدالة
PluginViewController::toolPageAction()فيmelis-react-overrideمنطقة واحدة بالضبط (تُحلّ من?key=<melisKey>) بوصفها صفحة HTML مستقلّة وتُعيدها معX-Frame-Options: SAMEORIGIN. وتفرضX-Requested-With: XMLHttpRequestبحيث تُصيَّر المناطق التي لهاfollow_regular_rendering:falseبطريقة AJAX، وتثبّت معرّف جلسة PHP عبر التصيير، وتحقن ملفات JS/CSSressourcesالخاصة بوحدة الأداة (يحملbundle.jsالأساسي أدوات MelisCore فقط)، وتتحايل على قائمة طويلة من غرائب النظام القديم بحيث تبدو الأداة داخل الإطار وتتصرّف تمامًا كما لو كان الوصول مباشرًا عبر/melis(بجداول DataTables الخاصة بها والنوافذ المنبثقة وتنبيهات gritter والتحقق لكل حقل).
يخدم الغلاف أصول المنصّة لتلك الـ iframes عبر MelisReactOverride\Service\PlatformAssetsService::build() — نفس CSS/JS الذي يُحمّله layoutCore.phtml الكلاسيكي — بحيث تُقلِع أداة قديمة بطريقة مطابقة داخل غلاف React.
المصادقة والصلاحيات
تُدار عملية تسجيل الدخول (Login) بواسطة MelisCoreAuth (MelisCoreAuthService)، وهي خدمة مصادقة من Laminas تعمل فوق جدول melis_core_user (usr_login / usr_password، تشفير bcrypt عبر password_hash). تُخزَّن الهوية الموثّقة — بما فيها usr_rights للمستخدم — في الجلسة. يقود غلاف React نفس المصادقة (يُصيّر شاشة تسجيل الدخول الخاصة به لكنه يُرسل إلى نفس الخدمة)؛ ويُرجِع GET /me الهوية بمجرد المصادقة، ونقطتا GET /langs وقراءة هوية لوحة تسجيل الدخول هما نقطتا النهاية العامتان الوحيدتان قبل تسجيل الدخول.
تُبوِّب الصلاحيات (Rights) المكتب الخلفي. يقرأ MelisCoreRights (MelisCoreRightsService) قيمة usr_rights للمستخدم — وهي قائمة سماح (allow-list) بصيغة XML — ليقرّر ما هو مرئيّ وقابل للإرسال:
- أقسام القائمة اليسرى التي يراها المستخدم هي عُقَد
*_toolstree_sectionالمُدرَجة في صلاحياته (isAccessible())؛ وXML صلاحيات فارغ يعني وصولًا كاملًا. في غلاف React يحدث نفس الترشيح على جانب الخادم فيGET /menu، الذي يُصدِر فقط العُقَد التي يجوز للمستخدم الوصول إليها (canAccess). - الأداة التي يفتقر المستخدم إلى صلاحياتها تُنتِج "You don't have access to this tool".
- تُقيم الصلاحيات على
melis_core_user.usr_rights؛ وبالنسبة للمستخدمين المستندين إلى أدوار يمكن أن تأتي من الدور (melis_core_user_role). يُحدّثMelisCoreCheckUserRightsListenerهذه الصلاحيات دوريًا ويُسجّل خروج المستخدم إذا أصبحت قيمةusr_statusغير نشطة.
الصلاحيات المتقدّمة (القدرات). يضيف المكتب الخلفي بلغة React طبقة أدقّ تابعة لـ التحقق من الوصول إلى الأداة: قدرات (capabilities) لكل أداة (list / create / edit / delete، أو تبويبات متداخلة). تُعلِن الوحدات عن القدرات الموجودة عبر config/react.capabilities.php؛ والمُحلّل MelisReactApi\Service\Capabilities هو السماح افتراضيًا (default-allow) — لا تُرفَض قدرة إلا إذا كانت مُعلَنة وموجودة معًا في قسم مخصّص <meliscore_tool_capabilities> من XML الصلاحيات. تُبوِّب متحكّمات الأدوات إجراءاتها عبر CapabilityGuardTrait::denyUnlessCan($cap) (يتجاوزها المسؤولون admins)، وتصل خريطة السماح نفسها إلى جانب العميل في GET /me لإخفاء عناصر الواجهة. يقع هذا في melis-react-api؛ وتُحرَّر قائمة المنع (deny-list) في Users → Rights.
منح الوصول إلى أداة جديدة
بعد إضافة أداة، امنح الوصول عبر Users → Rights في المكتب الخلفي بلغة React (مصفوفة صلاحياتها المتقدّمة تُغذَّى بواسطة GET /rights/capabilities)، أو احقن القسم في XML الصلاحيات عبر عملية ترحيل (migration) — انظر flyway/sql/V3__add_melisai_rights.sql.
دورة حياة طلب المكتب الأمامي
بالنسبة لعنوان URL عام، يُصيّر MelisFront + MelisEngine صفحة CMS (دون تغيير في v6):
- التوجيه (Routing) — يتطابق
melis-frontمع…/id/{idpage}. تُحلّ عناوين URL الصديقة لمحركات البحث (/about-us) إلى معرّف صفحة بواسطةMelisFrontSEORouteListener(يستعلم جدول SEO الخاص بالصفحات ويسجّل مسارًا ديناميكيًا وقت تحميل الوحدة). - الإرسال (Dispatch) — تختار المستمعات الأمامية المخطّط الأمامي وتستشير التخزين المؤقت للصفحة.
- تحميل الصفحة — يُرجِع
MelisEngine\Service\MelisPageService::getDatasPage($idPage, $type)كائنMelisPage(بيانات شجرة الصفحة + القالب)، مُخزَّنًا مؤقتًا تحتgetDatasPage_{id}_{type}. - تصيير القالب — يُصيّر متحكّم/إجراء
ZF2الخاص بالقالب ملف.phtmlالخاص بوحدة الموقع؛ وتُملأ مناطقMelisTagوإضافاتMelisDragDropZoneمن المحتوى المنشور.
GET /about-us
→ MelisFrontSEORouteListener maps /about-us → idpage=5
→ MelisFront\Controller\Index::index(idpage=5)
→ MelisPageService::getDatasPage(5, 'published') (cached)
→ template ZF2 controller/action → site .phtml → MelisTag / MelisDragDropZone
→ responseيتغلّب مسار regex عالي الأولوية
/melis-reactعمدًا على هذا المسار الشامل (catch-all)، بحيث يُحلّ إعادة تحميل صفحة كاملة على رابط عميق لـ React إلى الـ SPA بدلًا من "404 page not found".
التخزين المؤقت (Caching)
يخزّن Melis مؤقتًا بشكل مكثّف عبر عمليات تخزين مؤقت على نظام الملفات تحت cache/:
| التخزين المؤقت | ما يحتويه |
|---|---|
meliscore_platform_cache-* | مناطق المكتب الخلفي المُصيَّرة / إعدادات المنصّة |
meliscms_page-*، melisfront_pages_file_cache-* | صفحات CMS المُصيَّرة |
cache/config/ | إعدادات Laminas المدمجة (فقط إذا كان config_cache_enabled) |
datasource-*، melistoolcreator-* | عمليات تخزين مؤقت خاصة بالوحدات |
MelisCoreCacheSystemService هو واجهة برمجة التخزين المؤقت (getCacheByKey/setCacheByKey/ deleteCacheByPrefix). تُبطَل عمليات التخزين المؤقت عند الأحداث الرئيسية (تغييرات الوحدات، نشر الصفحة، تحديثات الصلاحيات) ويمكن مسحها يدويًا بحذف مجلدات cache/* المعنية — انظر استكشاف الأخطاء وإصلاحها. تضيف طبقة React عمليات تخزين مؤقت رخيصة خاصة بها: تُخدَم حزمة الاكتشاف بصفتها غير قابلة للتغيير (immutable) بتوقيع محتوى، ويحفظ PlatformAssetsService ذاكرة CSS/JS المدمجة للوحدات (مُعيدًا توليد etc/bundles/ إذا مسحتها أداة الوحدات).
الأحداث (Events)
Melis مدفوع بالأحداث بشكل كبير. تربط الوحدات المستمعات في Module::onBootstrap() (وعبر مدير الأحداث المشترَك) بدورة حياة MVC (EVENT_ROUTE، EVENT_DISPATCH، EVENT_RENDER، EVENT_FINISH) وبـ أحداث نطاق Melis (مثل melis_core_auth_login_ok، أحداث حفظ الصفحة). تُوسّع خدمات النطاق الصنف MelisGeneralService، الذي يضيف sendEvent() بحيث يمكن لأي خدمة أن تنشر أحداثًا تشترك فيها وحدات أخرى. هذه هي آلية التوسيع الأساسية المفكوكة الارتباط — فضّل مستمعًا على تعديل وحدة أخرى.
تتّبع طبقة React الفلسفة نفسها "راكِم، لا تتجاوز": فبدلًا من ترميز غرائب كل أداة بشكل صلب، يكشف melis-react-override خطّاف toolpage_extensions الذي يمكن لأي وحدة تنفيذه لتعديل HTML أداة قديمة أو أصولها داخل الإطار (يستخدمه مثلًا melis-ai-community-extensions).
الطلب، في صورة واحدة
public/index.php
→ application.config.php (MelisModuleManager assembles modules + platform DB config)
→ Laminas MVC run
├── /melis-react → SpaController serves the React shell (public)
│ → boot JSON: /me /menu /langs /assets /react-modules
│ → brick, or iframe → /melis/react-tool-page?key=<melisKey>
├── /melis… → auth (checkIdentity) → rights → PluginViewController zones (forwards)
└── public URL → MelisFront route (id/SEO) → MelisEngine page load → site template
→ caching at every expensive step (MelisCoreCacheSystemService)
→ responseالملفات الرئيسية
| الشأن | المسار |
|---|---|
| إعدادات التطبيق / الإقلاع | config/application.config.php |
| تجميع الوحدات | vendor/melisplatform/melis-core/src/MelisModuleManager.php |
| إقلاع / مستمعات النواة | vendor/melisplatform/melis-core/src/Module.php |
| المصادقة | vendor/melisplatform/melis-core/src/Service/MelisCoreAuthService.php |
| الصلاحيات | vendor/melisplatform/melis-core/src/Service/MelisCoreRightsService.php |
| شجرة الإعدادات | vendor/melisplatform/melis-core/src/Service/MelisCoreConfigService.php |
| تصيير المناطق | vendor/melisplatform/melis-core/src/Controller/PluginViewController.php |
| واجهة برمجة التخزين المؤقت | vendor/melisplatform/melis-core/src/Service/MelisCoreCacheSystemService.php |
| توجيه المكتب الأمامي / SEO | vendor/melisplatform/melis-front/src/Listener/MelisFrontSEORouteListener.php |
| خدمة الصفحة | vendor/melisplatform/melis-engine/src/Service/MelisPageService.php |
| واجهة JSON لـ React | vendor/melisplatform/melis-react-api/src/Controller/MelisReactApiController.php |
| مُحلّل القدرات | vendor/melisplatform/melis-react-api/src/Service/Capabilities.php |
| الـ SPA + iframe القديمة | vendor/melisplatform/melis-react-override/src/Controller/ |
| غلاف React (مصدر الـ SPA) | vendor/melisplatform/melis-core/ui-react/ |