שתי מחלקות קטנות נושאות את ההגדרות שנערכות בלי לגעת בקוד. PARAMS היא טבלת מפתח וערך של הגדרות אתר (תבנית דוא"ל עוטפת, אסימוני שירותים, דגלים), ו-LANGS היא מילון של מילים וטקסטים קבועים בכמה שפות. בעמוד הזה תראו איך שתיהן נטענות, איך קוראים אותן ושומרים אליהן, ומתי צריך לנקות מטמון כדי שהשינוי יופיע. פירוט הסכמות והמפתחות מופיע ברפרנס פרמטרים ושפות.
system/ בעמוד הזה הוא תיקיית הליבה הפרוסה (api/core בריפו), ו-{admin} הוא CONFIG::$admin_url.PARAMS: הגדרות מפתח-ערך#
איך נטען#
בתחילת כל בקשה PAGE::load קורא ל-PARAMS::init(). הפונקציה טוענת את כל השורות של CRM_params מהמטמון (מפתח params, תוקף 30 יום, cache_engine::get("params", ..., 24*30, false)) ושומרת אותן במערך סטטי בזיכרון. מכאן, קריאה של פרמטר במהלך הבקשה אינה פונה למסד הנתונים.
קריאה וכתיבה בזיכרון#
| פונקציה | תיאור |
|---|---|
PARAMS::get($key) | הערך כמחרוזת, או "" אם המפתח לא קיים. אין הבחנה בין "לא קיים" ל"ריק" |
PARAMS::set($key, $value) | מגדיר ערך בזיכרון בלבד, לבקשה הנוכחית. לא נכתב למסד ולא למטמון |
PARAMS::GetArray($onlyWords = false) | שבורה, ראו אזהרה |
$tpl = PARAMS::get("mail_tpl");
if ($tpl === "") {
// אין תבנית עוטפת, ממשיכים בלי
}
if (PARAMS::get("poolse_mail") === "0") {
// המפתח קיים והערך "0" (שימו לב להשוואת מחרוזות)
}
בתבניות Smarty אפשר לקרוא לאותה פונקציה ישירות, כמו שעושה תבנית הליבה views/admin/sqllog.tpl:
{if PARAMS::get('log_sql') == '1'}הרישום פעיל{else}הרישום כבוי{/if}
GetArray לא עובדת
הפונקציה מפנה ל-self::$arrWords, שלא קיים במחלקה PARAMS (המילים נמצאות ב-LANGS). קריאה אליה גורמת לשגיאה קטלנית. כדי לקבל את כל המילים השתמשו ב-LANGS::get_words_array().value הוא TEXT, ולכן PARAMS::get תמיד מחזירה מחרוזת. דגל בוליאני נשמר כ-1 או 0, ומשווים אליו כמחרוזת. מבנה מורכב נשמר כ-JSON ומפוענח בקוד.הטבלה CRM_params#
| עמודה | משמעות |
|---|---|
id | מזהה |
sysName | המפתח (ייחודי, עד 100 תווים) |
value | הערך (TEXT) |
description | תיאור חופשי, מוצג בפאנל |
perm_developer | 1 מסתיר את השורה ממנהלים שאינם מפתחים בפאנל Params |
מפתחות שהליבה עצמה משתמשת בהם:
| מפתח | שימוש |
|---|---|
attaches_version | מספר גרסה לנכסים סטטיים, עולה בניקוי מטמון (ראו צינור הנכסים) |
log_sql | 1 מפעיל רישום שאילתות (ראו דיבוג מסד נתונים) |
cron_runner_token, cron_runner_heartbeat | טוקן ודופק של משימות מתוזמנות |
mail_tpl | נתיב תבנית עוטפת לדוא"ל |
poolse_mail, poolse_mail_exclude, poolse_api_key, poolse_api_url | הגדרות POOLSE (ראו דוא"ל) |
file_manager_readonly, file_manager_extra_roots, file_manager_show_sensitive | מנהל הקבצים (ראו מנהל קבצים) |
seo_*, todo_*, bot_api_secret | מערכות SEO, אינטגרציות וסוכן ה-AI |
עריכה בפאנל#
הפאנל {admin}/Params מציג את השורות (קודם perm_developer ואחר כך לפי sysName), ומאפשר עריכת הערך ישירות בטבלה. עריכה, הוספה ומחיקה מנקות את מטמון params אוטומטית. מי שאינו מפתח (ADMIN::is_developer()) רואה רק שורות עם perm_developer = 0, ולא רואה את עמודת ההרשאה.
perm_developer מסתיר, לא מצפין
הערכים נשמרים בטקסט גלוי במסד הנתונים. הדגל מסתיר שורה מתצוגת הפאנל בלבד. אין להשתמש ב-CRM_params ככספת: סיסמאות ומפתחות API ישמרו במקום שאפשר להגביל גישה אליו, ולא בשורה שכל גיבוי של המסד יכלול (ראו הקשחה).כתיבה מהקוד#
כתיבה מהקוד היא עדכון שורה ואחריו ניקוי מטמון. בלי הניקוי, אחרי עד 30 יום (או עד הניקוי הבא) הבקשות ימשיכו לקבל את הערך הישן.
function save_param($name, $value, $description = "")
{
$row = DB::get_val("params", ["sysName" => $name]);
$u = DB::update("params")->set("value", (string)$value);
if ($row) {
$u->update((int)$row["id"]);
} else {
$u->set("sysName", $name)
->set("description", $description)
->set("perm_developer", 0);
$u->insert();
}
cache_engine::remove("params");
PARAMS::set($name, (string)$value); // גם בבקשה הנוכחית
}
ה-builder מקבל את שם הטבלה בלי CRM_ ומטפל ב-escape (ראו בנאי שאילתות). כשמוסיפים פרמטר חדש כחלק מהתקנה, עדיף INSERT ... ON DUPLICATE KEY UPDATE, כי sysName ייחודי:
INSERT INTO CRM_params (sysName, value, description, perm_developer)
VALUES ('my_feature_enabled', '1', 'הפעלת הפיצ''ר החדש', 0)
ON DUPLICATE KEY UPDATE description = VALUES(description);
PARAMS::get מחזירה "" כשהמפתח חסר, ולכן כתבו את הקוד כך שמחרוזת ריקה היא ברירת המחדל הסבירה: $limit = (int)PARAMS::get("my_limit") ?: 20;.LANGS: מילון הממשק#
איך נטען#
בהמשך אתחול הבקשה, אחרי PARAMS::init() וטעינת ההקשר של העמוד, PAGE::load קורא ל-LANGS::init_words(). הפונקציה טוענת את כל המילים בשפה הנוכחית (CONFIG::$lang) מהמטמון words_lang<N> (תוקף 4 שעות), ושומרת במערך בזיכרון. הטעינה היא DB::query("langs_words") עם השדות sysName, value; הצטרפות הערכים לשפה נעשית אוטומטית דרך טבלת התוכן (ראו מסד נתונים רב-לשוני).
השפה היא מזהה פלטפורמה (CRM_platforms.id, ראו פלטפורמות ורב-לשוניות), ולכל פלטפורמה מילון משלה.
הטבלאות#
| טבלה | עמודות |
|---|---|
CRM_langs_words | id, sysName (המפתח, למשל EDIT), group_id |
CRM_langs_words_content | id, parentId (מצביע ל-langs_words.id), langId (פלטפורמה), value (HTML). הצירוף parentId, langId ייחודי |
CRM_langs_words_groups | id, name: קבוצות לארגון המילים בפאנל |
קריאה#
| פונקציה | תיאור |
|---|---|
LANGS::get_word($key, $lang = false) | בלי $lang: המילה בשפה הנוכחית מהזיכרון, או "" אם המפתח חסר. עם $lang: שאילתה למסד של המילה בשפה אחרת (מזהה פלטפורמה) |
LANGS::translate($val) | מחפש $val כ-ערך במילון (array_search רופף) ומחזיר את הערך שנמצא. בפועל התוצאה זהה לקלט, ראו הערה |
LANGS::set_word($key, $value) | מגדיר מילה בזיכרון בלבד, לבקשה הנוכחית |
LANGS::init_words() | טעינה מהמטמון או מהמסד (נקראת מ-PAGE::load) |
LANGS::get_words_array() | כל המילים כמערך sysName => value |
echo LANGS::get_word("EDIT"); // בשפת הבקשה הנוכחית
echo LANGS::get_word("EDIT", 2); // בשפה 2, שאילתה למסד בכל קריאה
בתבניות Smarty:
<button>{LANGS::get_word('EDIT')}</button>
get_word בלי $lang מחזירה "" כשהמפתח לא קיים, ולא את שם המפתח. כך ממשק עם מפתח חסר נראה ריק ולא שבור. הוסיפו את המפתח לכל השפות, או בדקו תוצאה ריקה בקוד.get_word עם שפה היא שאילתה בכל קריאה
הקריאה עם $lang לא משתמשת במטמון, ופונה למסד בכל פעם. בלולאה על הרבה מילים זה יוצר עומס. כשצריך הרבה מילים בשפה אחרת, בצעו שאילתה אחת על CRM_langs_words_content לפי langId. בנוסף, אם המילה לא קיימת באותה שפה, הפונקציה לא בודקת את התוצאה לפני שהיא ניגשת לשדה value, ויכולה להפיק אזהרת PHP.translate לא מתרגמת
היא מחפשת את הטקסט שקיבלה בין הערכים של המילון ומחזירה את הערך שמצאה, כלומר את אותו טקסט (או את הקלט עצמו כשלא נמצא). היא סורקת את כל המילון בכל קריאה. לתרגום לפי מפתח השתמשו ב-get_word.עריכה#
מילים נערכות בפאנל הניהול הוותיק ui_langs_legacy, שבו קבוצות (insertGroup), מילים (insertWord) ורשימת מילים בקבוצה. כל שמירה מנקה את מטמון המילים (cache_engine::remove("words")), מרעננת אותו (LANGS::init_words()) ומפעילה את האירוע words_update (PAGE::trigger).
כתיבה מהקוד#
הוספה של מילה חדשה היא שורה ב-CRM_langs_words ושורת ערך לכל שפה ב-CRM_langs_words_content. בסקריפט התקנה אפשר לכתוב את זה כך:
INSERT INTO CRM_langs_words (sysName, group_id) VALUES ('CHECKOUT_TITLE', 1);
INSERT INTO CRM_langs_words_content (parentId, langId, value)
SELECT id, 1, 'סיכום הזמנה' FROM CRM_langs_words WHERE sysName = 'CHECKOUT_TITLE' LIMIT 1
ON DUPLICATE KEY UPDATE value = VALUES(value);
ואחרי כל שינוי, מנקים את המטמון:
cache_engine::remove("words");
הפרמטר הוא קידומת של שם קובץ המטמון, ו-words תופס את words_lang1, words_lang2 וכן הלאה בכל תיקיות המטמון.
טקסט שמוצג למשתמש ושייך לממשק, ולא לתוכן של דף, שייך למילון כדי שיהיה אפשר לתרגם ולערוך אותו בלי קוד (ראו תקן קוד).
מתי מנקים מטמון#
| שינוי | מה לעשות |
|---|---|
ערך ב-CRM_params דרך הפאנל | כלום, הפאנל מנקה |
ערך ב-CRM_params מ-SQL או מקוד | cache_engine::remove("params") |
מילה דרך ui_langs_legacy | כלום, הפאנל מנקה |
| מילה מ-SQL או מקוד | cache_engine::remove("words") |
| שינוי אינו מופיע | ודאו שניקוי המטמון רץ על אותו שרת ותיקיית מטמון, ראו ניהול מטמון |
מנגנון המטמון שומר ערך רק אם מספר שגיאות ה-DB לא עלה בזמן בנייתו, כך שתקלה רגעית לא "נתקעת" במטמון עד פקיעת התוקף (ראו API המטמון).