העמוד הזה מרכז את השמות והכללים שחוזרים בכל הליבה ובכל אתר. הוא מחלק אותם לשני סוגים, כי זה ההבדל בין "נשבר" ל"לא נעים":
- נאכף על ידי הקוד: אם לא תעקבו אחריו, משהו נכשל בפועל (שאילתה, ניתוב, טעינת קובץ).
- מוסכמה: הצוות עובד כך כדי שהקוד יהיה אחיד וקל לתחזוקה. שום דבר לא ישבר אם תחרגו, אבל עדיף לא.
נתיבי הקבצים הם יחסית ל-api/core בריפו, שהיא system/core/ באתר פרוס. {admin} הוא CONFIG::$admin_url. תקן הכתיבה המלא (עיצוב קוד, הערות, בדיקות) נמצא ב-תקן הקוד, והמחלקות שמוזכרות כאן ב-ה-Collections.
מסד הנתונים#
שמות טבלאות#
| כלל | פירוט | סוג |
|---|---|---|
כל טבלה מתחילה ב-CRM_ | CONFIG_USER::$db_prefix מגדיר CRM, ו-CONFIG::$db_fullprefix הוא CRM_. בפועל חייבים להשאיר CRM: עשרות שאילתות בליבה כותבות CRM_ קשיח, למשל SELECT * FROM CRM_platforms ב-CONFIG::init | נאכף |
ה-API של DB מקבל שם בלי קידומת | DB::query("pages"), DB::update("params"), DB::get_val("adminPanel_admins", 3) | נאכף |
| שאילתה גולמית דורשת קידומת מלאה | DB::sql("SELECT * FROM CRM_pages WHERE id = " . (int)$id), DB::get_all(...), DB::get_row(...) | נאכף |
קלט משתמש בשאילתה גולמית עובר ב-DB::escape | או ב-(int) למספרים. בונה השאילתות מנקה ערכים של מערך התנאים, אבל לא מפתחות | נאכף כדי לא לייצר SQLi |
שמות באנגלית, snake_case | cron_tasks, minify_groups. יש חריגים היסטוריים בסגנון adminPanel_admins ו-seoUrl | מוסכמה |
// בונה: בלי קידומת
$row = DB::query("pages", ["id" => (int)$id])->get_row();
// גולמי: עם CRM_ ועם escape
$res = DB::sql("SELECT * FROM CRM_pages WHERE slug = '" . DB::escape($slug) . "'");
DB::insert לא קיימת. הוספה נעשית דרך DB::update("table")->set_var("col", $v)->insert() (ו-set() הוא כינוי ל-set_var()), ועדכון דרך ->update($id).
טבלאות _content ועמודות קבועות#
| מוסכמה | פירוט | סוג |
|---|---|---|
id INT AUTO_INCREMENT PRIMARY KEY | הבונים מניחים שבכל טבלה יש id (DB::get_val, DB::delete, ->update($id)) | נאכף |
טבלת תוכן רב-לשונית: <table>_content | אם קיימת טבלה בשם <t>_content, ה-JOIN נבנה אוטומטית על parentId (מצביע ל-<t>.id) ו-langId (מזהה פלטפורמה). DB::delete מוחק גם ממנה | נאכף |
ord לסדר ידני | panel_table עם order_manager מוסיף עמודת ord INT בעצמו אם חסרה (ALTER TABLE בזמן ריצה) | מוסכמה |
is_active מול active | רוב הטבלאות משתמשות ב-is_active (יותר ממאה שימושים בליבה, מול כ-20 של active). modules.active ו-users.active הן חריגות שהקוד קורא בשמן. לא להחליף ביניהן | נאכף לפי הטבלה |
שורה בטבלה עם _content נעלמת מתוצאות DB::query אם אין לה שורת תוכן בשפה המבוקשת, כי ה-JOIN הוא פנימי. הפירוט ב-טבלאות רב-לשוניות וב-סכמת הטבלאות.
langId לטבלה שלא נקראת _content
רק טבלה ששמה מסתיים ב-_content מקבלת סינון langId. טבלת צד אחרת מקבלת JOIN בלי סינון שפה, ותקבלו שורה לכל שפה.הפורמט |1||2|#
רשימות שמורות בעמודה אחת (דומיינים מורשים, תגיות, בחירה מרובה) בפורמט שבו כל ערך עטוף בקווים אנכיים ומופרד ב-||:
MISC::bgl_implode(["a", "b"]); // "|a||b|"
MISC::bgl_explode("|a||b|"); // ["a", "b"]
הפורמט מאפשר LIKE '%|a|%' מדויק, בלי להתבלבל בין 1 ל-11. כללים לזכור:
bgl_implodeמסננת ערכים ריקים עםarray_filter, ולכן גם את הערך0וגם את"0". מזהה אפס לא יישמר.bgl_explodeמחזירה את תוצאתarray_filter, כלומר המפתחות לא מסודרים מחדש. אם צריך אינדקסים רצופים, השתמשו ב-array_values.- קבוצה ריקה נשמרת כמחרוזת ריקה, לא כ-
"||".
קבצים ומחלקות#
| מה | איפה | שם הקובץ והמחלקה | סוג |
|---|---|---|---|
| קונטרולר של אתר | application/controllers/<name>.php | מחלקה בשם <name> שיורשת מ-bgl_controller. MODULE::get עושה new $module | נאכף |
| קונטרולר מערכת | system/core/controllers/<name>.php (כתובת /system/<name>) | אותו כלל | נאכף |
| פאנל ניהול | application/admin/<panel>.php (או admin/<panel>.php בליבה) | מחלקה ADMINMODULE_<panel> שיורשת מ-bgl_controller. שם הקובץ הוא panel_name בטבלה adminPanel_panels | נאכף |
| מודל | CONFIG::$models_folder/<path>.php | המחלקה נקראת כשם הקובץ (basename($path)) והמופע מוצב על הקונטרולר: $this->load->model("m_news") ואז $this->m_news | נאכף |
| ספריית ליבה | system/core/libraries/<path>.php | $this->load->library("name") טוען מהליבה בלבד ולא יוצר מופע | נאכף |
| ערכת נושא | themes/<name>/index.php + index.tpl | מחלקה TEMPLATE_<name> שיורשת מ-bgl_theme | נאכף |
| תבנית של קונטרולר | application/views/<tpl>.tpl | נטענת עם $this->view("<tpl>", $vars). תבנית של הליבה: system_view | נאכף |
| קובץ אתחול של האתר | application/includes/*.php | נטענים בכל בקשה, בסדר אלפביתי, פעם אחת (FILES::include_dir) | נאכף |
הערות שחוזרות בכמה מהשורות:
- שמות המחלקות של קונטרולר הם שם הקובץ בדיוק, כולל רישיות. בשרת Linux
News.phpו-news.phpשונים. - קונטרולר של אתר פועל רק אם יש לו שורה פעילה בטבלה
modules(modules.activeשווה"1"). קובץ בלי שורה נותן 404, וזו הסיבה לרישום ב-CRM_modules. ראו קונטרולרים של אתר. - מתודה בקונטרולר שמחזירה את המחרוזת
"ERRORPAGE"מפעילה 404 דרךMODULE::errorpage(). ב-AdminModuleאותה מחרוזת היא סימון למודול לא קיים. ראו 404, 503 ושגיאות. -
bgl_controller, bgl_modelו-bgl_loaderהם השמות שקוד אתר יורש מהם, ו-wz_*הם המחלקות שמאחוריהם.
קובץ application/admin/<panel>.php לבדו לא מספיק. הפאנל מופיע בתפריט רק כששורה ב-adminPanel_panels מצביעה עליו. ראו בניית פאנל.
כתובות ופרמטרי $_GET שקובע ה-ROUTER#
ROUTER::parse_friendly_url הוא המקום היחיד שכותב את הפרמטרים שהקונטרולר קורא. אל תחפשו אותם ב-.htaccess, כי הוא רק מעביר הכול ל-index.php.
| פרמטר | מתי מוגדר | משמעות |
|---|---|---|
$_GET["module"] | כתובת אתר <module>/<page>/<id>, וגם בפאנל {admin}/<module>/... | שם הקונטרולר (באתר) או הפאנל (בניהול) |
$_GET["pname"] | הסגמנט השני | שם המתודה (ברירת מחדל index) |
$_GET["id"] | הסגמנט השלישי | מזהה או שאר הנתיב |
$_GET["sys_controller"] | כתובת system/<name>/... | שם קונטרולר המערכת |
CONFIG::$system_type | "admin" כשהכתובת מתחילה ב-{admin}, אחרת "client" | צד הבקשה |
כתובות ידידותיות ממפות לאותם פרמטרים דרך הטבלה seoUrl. ראו ניתוב (ROUTER) ו-כתובות ידידותיות. דף הבית (url ריק) משתמש ב-CONFIG::$default_module.
שמות סטטיים ומפתחות מטמון#
| כלל | דוגמה | סוג |
|---|---|---|
תכונות סטטיות ב-snake_case | CONFIG::$platform_data, PAGE::$cache_this_page, DB::$error_count | מוסכמה |
מתודות סטטיות ב-snake_case | PAGE::add_asset, cache_engine::get_stats, ROUTER::parse_friendly_url | מוסכמה |
| שמות מחלקות סטטיות באותיות גדולות | DB, PAGE, MISC. חריגות: cache_engine, bgl_*, wz_* | מוסכמה |
| מפתח מטמון גלובלי הוא מחרוזת קבועה | platforms, params, table_list, redirections, minify_files, minify_groups | מוסכמה |
| מפתח שתלוי שפה מקבל את ה-id של הפלטפורמה כסיומת | words_lang<id>, modules_lang<id> | מוסכמה |
| מפתח שתלוי טבלה מקבל את שמה | table_columns_<table> | מוסכמה |
בקריאה ל-cache_engine::get($key, $func, $hours, $in_platform) הפרמטר $in_platform קובע אם הערך נשמר בתיקייה של הדומיין הנוכחי או בשורש המטמון. מפתחות שתלויים בשפה צריכים להישאר ברירת מחדל (true) כדי שדומיין אחד לא ידרוס את השני. הפירוט ב-איך המטמון עובד, ב-cache_engine API וב-פלטפורמות ורב-לשוניות.
צד הלקוח: SCSS, RTL, FontAwesome ו-Vue#
SCSS#
עורכים קבצי .scss בלבד. הליבה מקמפלת אותם בשרת (FILES::scss), והכתובת foo.scss.css מוגשת מהמטמון או מתקמפלת בבקשה הראשונה (ראו צינור הנכסים). קובץ .css שנוצר מהקומפילציה ייכתב מחדש, ושינוי ידני בו אובד. זה כלל נאכף בפועל, כי אין מקור אחר לאמת.
RTL#
| כלל | פירוט | סוג |
|---|---|---|
| מאפיינים לוגיים בלבד | margin-inline-start/end, padding-inline-start/end, inset-inline-start/end, text-align: start/end. לא left ו-right פיזיים | מוסכמה |
end אינו right | ב-RTL הצד inline-end הוא left. בחרו לפי המשמעות ולא לפי הצד שאתם רואים במסך | מוסכמה |
| כיוון העמוד מגיע מהפלטפורמה | <html dir="..."> ו-class rtl או ltr על <body>, שניהם מ-CRM_platforms.direction | נאכף |
| בפאנל הניהול כותבים את שני הסלקטורים | הכיוון מסומן גם כ-dir על <html> וגם כ-class על <body>, וקובצי ה-SCSS של הפאנל כותבים [dir="rtl"] &, .rtl & | מוסכמה |
מאפיין לוגי נגזר מהכיוון של האלמנט עצמו: padding-inline-end על תא עם direction: ltr ינחת בצד הפיזי הנגדי למה שציפיתם. בדקו בדפדפן כשמערבבים כיוונים. ראו גם את הערה על <html lang> ב-פלטפורמות ורב-לשוניות.
FontAwesome#
פאנל הניהול טוען FontAwesome Free 5.15.4 כקובץ assets/fontawesome/js/all.min.js (בגרסת JS שמחליפה תגיות <i> ב-<svg>). שני כללים נגזרים:
- שמות של אייקונים מגרסה 6 לא יוצגו. השתמשו בשמות של גרסה 5 (
fas fa-..., far, fab). - לעולם לא לשים
v-if, v-elseאוv-forעל התג<i>של האייקון. ה-JS מחליף אותו ב-<svg>, ו-Vue מאבד את הצומת שהוא מנסה לעדכן, ורכיב שלם מפסיק להתרנדר. עוטפים:
<span v-if="saved" class="wz_if"><i class="fas fa-check"></i></span>
אל תבנו ספינר מאייקון של FontAwesome. השתמשו באלמנט CSS רגיל, למשל <span v-if="loading" class="wz_spin"></span>.
wz_if ו-wz_spin הם שמות מוסכמים של הצוות
אין להם הגדרה בקוד של api/core. wz_if הוא class מזהה על העטיפה, ו-wz_spin הוא class של ספינר שאתם מגדירים בעצמכם ב-SCSS. מה שחשוב הוא העטיפה והימנעות מ-v-if על <i>, לא השמות.Vue#
רכיבי Vue של הפאנל כתובים ב-Vue 3 בסגנון Options API (export default { data() {...}, methods: {...} }, או אובייקט אפשרויות שמועבר ל-createApp), והספרייה נטענת כקובץ vue.global.min.js מהערכה admin_panel. הליבה מכילה רכיב .vue אחד, ו-FILES::vue($url, $m_settings) טוענת חבילה בנויה: קובץ HTML שה-<head> שלו מצביע על קובצי ה-JS וה-CSS.
פאנל שנטען בניווט AJAX של הניהול מוחלף בלי רענון עמוד, ולכן הוא צריך להרכיב את עצמו בכל כניסה ולפרק את עצמו ביציאה. מנהל הקבצים (assets/file_manager/app.js) עושה את זה עם mountApp() ו-unmountApp(), ועם MutationObserver על #mainframe שמזהה מתי הפאנל נכנס או יצא. זה הדפוס לחקות. ראו פאנלים מבוססי Vue.
מילים, פרמטרים ו-JSON#
מילים (LANGS)#
| כלל | פירוט | סוג |
|---|---|---|
מפתח מילה הוא sysName | LANGS::get_word('EDIT'). מילה חסרה מחזירה מחרוזת ריקה ולא שגיאה | נאכף |
| אותיות גדולות עם קווים תחתונים | EDIT, ARE_YOU_SURE, PANEL_MAIN. מילים של הליבה מתחילות לעיתים ב-_ ומסתיימות בו: _MESSAGE_ADDED_, _SEND_ | מוסכמה |
| הערך נשמר בטבלת התוכן | langs_words_content.value (עמודות parentId, langId, value) | נאכף |
| מילה לכל שפה | שורה ב-langs_words ושורה ב-langs_words_content לכל langId | נאכף |
הליבה אינה אחידה במלואה: יש מילים שנכתבות באותיות קטנות (add_correct_phone, _secCodeError_). אל תתבססו על רישיות כשאתם מחפשים מילה, והשתמשו בחיפוש בטבלה. ראו פרמטרים ומילים.
פרמטרים (PARAMS)#
מפתחות PARAMS נשמרים ב-params.sysName, כמעט תמיד ב-snake_case באותיות קטנות: attaches_version, log_sql, cron_runner_token. פרמטר חסר מחזיר מחרוזת ריקה. ראו פרמטרים ומילים.
תגובות JSON#
אין צורת תגובה אחת לכל המערכת. הדפוסים בקוד הליבה, לפי תדירות:
| דפוס | דוגמה |
|---|---|
error (נפוץ מאוד) | ["error" => "invalid_url"] או ["success" => false, "error" => $msg] |
ok (נפוץ מאוד) | ["ok" => true, ...] |
success | ["success" => true, ...] |
result ו-msg | ["result" => false, "msg" => LANGS::get_word(...)], בבדיקות של שדות טופס |
בקוד הליבה מתודות מחזירות JSON בשתי דרכים: return json_encode(...) או echo json_encode(...) ואז סיום. כדי שעברית לא תהפוך ל-\uXXXX הוסיפו JSON_UNESCAPED_UNICODE, כפי שעושה הקוד בכמה מקומות. ב-API חדש בחרו דפוס אחד (ok ו-error) ושמרו עליו. ראו JSON API של טפסים וטבלאות.
סיכום: מה נאכף ומה לא#
| נאכף (אחרת זה נשבר) | מוסכמה (אחרת זה פחות מסודר) |
|---|---|
קידומת CRM_ בכל טבלה | snake_case בשמות טבלאות ועמודות |
id בכל טבלה שעוברת בבונים | עמודת ord וכתיבת is_active |
| שם קובץ שווה לשם מחלקה | מאפיינים לוגיים ב-SCSS |
ADMINMODULE_<panel> ושורה ב-adminPanel_panels | שמות מילים באותיות גדולות |
שורת modules פעילה לכל קונטרולר אתר | דפוס תגובת JSON אחיד |
DB::escape על קלט בשאילתה גולמית | שמות wz_if ו-wz_spin |
אין v-if על <i> של FontAwesome | סגנון Options API בכל רכיב |