שכבת ה-DB

סקירה של מחלקת DB בליבת WIZZO CMS: החיבור הלזי ל-MySQL דרך PDO, הקידומת CRM_, ההבדל בין ה-builder לבין SQL גולמי, ומה אין בה (prepared statements וטרנזקציות).

⏱ 3 דק' קריאה 627 מילים ערוך דף זה ב-GitHub

כל גישה למסד הנתונים ב-WIZZO CMS עוברת דרך מחלקה סטטית אחת, DB, בקובץ api/core/collections/DB.php. בדף הזה תלמדו איך החיבור נוצר, איך מתייחסים לשמות טבלאות, מתי משתמשים ב-builder ומתי ב-SQL גולמי, ואילו מגבלות כדאי להכיר לפני שכותבים שאילתה ראשונה.

בעמוד הזה system/ הוא תיקיית הליבה הפרוסה (= api/core בריפו) ו-{admin} הוא CONFIG::$admin_url של האתר.

החיבור#

החיבור נפתח בעצלות (lazy): רק כשמריצים שאילתה ראשונה, DB::create_connection() יוצרת אובייקט PDO מפרטי החיבור שב-CONFIG. בפועל CONFIG::$db_type הוא "PDO" (MySQL / MariaDB). ענפי mysql הישן, pg ו-clickhouse קיימים בקוד אבל אינם בשימוש באתרים רגילים.

מיד אחרי החיבור הליבה מריצה:

set names utf8mb4
SET SESSION sql_mode = ''

כלומר הקידוד הוא utf8mb4, ו-strict mode כבוי: ערך שלא נכנס לעמודה נחתך בשקט במקום להחזיר שגיאה.

כשהחיבור נכשל

כשל בחיבור מפעיל MISC::service_unavailable(...) והאתר מחזיר 503. אל תתפסו את זה ב-try/catch בקוד שלכם: אין מה להמשיך בלי מסד נתונים.

בסוף הבקשה core.php קוראת ל-DB::close_connection(). אם צריך את אובייקט ה-PDO עצמו (טרנזקציה, lastInsertId), קבלו אותו עם DB::get_connection().

הקידומת CRM_#

כל טבלה של WIZZO CMS מתחילה ב-CRM_ (CONFIG::$db_prefix הוא "CRM" ו-CONFIG::$db_fullprefix הוא "CRM_"). אי אפשר לשנות את הקידומת: כ-80 מקומות בליבה כוללים את CRM_ ישירות בתוך מחרוזות SQL.

סגנוןשם הטבלהדוגמה
ה-builder (DB::query, DB::update, DB::delete, DB::get_val)בלי קידומתDB::query("pages")
SQL גולמי (DB::sql, DB::get_all, DB::get_row עם מחרוזת)עם הקידומת המלאה"SELECT * FROM CRM_pages"
// builder: שם טבלה חשוף
$page = DB::query("pages", ["id" => 12])->get_row();

// SQL גולמי: CRM_ מלא
$rows = DB::get_all("SELECT id, sysName FROM CRM_params ORDER BY sysName");

שתי דרכים לעבוד#

ה-builder מתאים לרוב הפעולות היומיומיות: קריאת שורה לפי תנאי שוויון, הוספה, עדכון ומחיקה, כולל טיפול אוטומטי בטבלאות תרגום (ראו טבלאות רב-לשוניות). הוא מעלים את הצורך לדעת איך נראית טבלת ה-_content.

SQL גולמי מתאים לכל מה שה-builder לא יודע: JOIN מפורש, GROUP BY מורכב, תנאים שאינם שוויון, UNION, פקודות DDL.

מה צריךהכלידף
לקרוא שורות או שורהDB::query() או DB::get_all() / DB::get_row()שאילתות
ערך בודדDB::get_val()שאילתות
להוסיף / לעדכןDB::update(table)->set_var(...)->insert() / ->update($id)בוני שאילתות
למחוקDB::delete()בוני שאילתות
פקודה חופשיתDB::sql()שאילתות
DB::insert לא קיים

אין מתודה DB::insert(...), וקריאה אליה היא fatal error. הוספת שורה נעשית כך: DB::update("table")->set_var("col", $val)->insert(). קבצי ליבה ישנים שקוראים ל-DB::insert הם קוד מת, אל תעתיקו מהם.

מה אין בשכבה הזאת#

  • אין prepared statements. DB::sql() מבצעת prepare ו-execute ללא פרמטרים מקושרים, ולכן כל ערך שמגיע מבחוץ חייב לעבור DB::escape() ולהיכנס בין גרשיים שאתם מוסיפים בעצמכם. ראו שאילתות.
  • אין API של טרנזקציות. משתמשים ב-DB::sql("START TRANSACTION") ו-COMMIT / ROLLBACK, או ב-DB::get_connection()->beginTransaction().
  • אין מנגנון migrations בקוד האתר. טבלאות נוצרות על ידי עדכון גרסה או ensure_* עצלים. ראו SQL להקמה.

שגיאות#

ב-PHP 8.3 PDO זורק חריגה על שאילתה שגויה, ו-DB::sql() זורקת אותה הלאה כ-Exception בפורמט "<הודעת הדרייבר> | <ה-SQL>". הערות בקוד שמדברות על "silent mode" הן שריד של PHP 7. פרטים נוספים ב-דיבוג SQL.

פונקציות עזר נוספות#

מתודהמה היא עושה
DB::escape($str)מחזירה את המחרוזת בלי גרשיים חיצוניים (PDO::quote עם קילוף). מחרוזת ריקה או NULL חוזרות כמות שהן
DB::is_table_exists($table)האם הטבלה (בשם חשוף, בלי CRM_) קיימת. הרשימה נשמרת במטמון ל-7 ימים תחת table_list
DB::is_column_exists($table, $column)האם עמודה קיימת. נשמר ב-table_columns_<table> ל-7 ימים
DB::num_rows($res)rowCount() של ה-statement
DB::had_error()true כש-DB::$error_count גדול מאפס
מטמון רשימת הטבלאות

מי שיוצר טבלה ידנית צריך למחוק את מטמון table_list (וגם platforms ו-params אם נגע בהם), אחרת is_table_exists ימשיך להחזיר את התשובה הישנה עד 7 ימים. ראו ניקוי ותחזוקה.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.