panel_table היא המחלקה שמציגה כמעט כל רשימה בפאנל הניהול: אתם נותנים לה שאילתה והגדרת עמודות, והיא מחזירה טבלה מלאה (אפליקציית Vue) עם מיון, דפדוף, סינון לפי עמודה, חיפוש חופשי, פעולות ויצוא. בדף הזה מוסבר איך מגדירים עמודות, סינון, פעולות וייצוא. את רשימת כל המאפיינים והמתודות תמצאו ב-רפרנס panel_table.
{admin} הוא CONFIG::$admin_url, ו-system/ הוא תיקיית הליבה (api/core בריפו). המחלקה נמצאת ב-system/libraries/panel_table.php ונטענת אוטומטית בבקשת אדמין.
דוגמה מינימלית#
public function index()
{
$tbl = new panel_table("redirections_list"); // מזהה ייחודי וקבוע
$tbl->title = "הפניות";
$tbl->query = DB::query("redirections");
$tbl->order_manager = false;
$tbl->btn_add_url = CONFIG::$admin_url . "/redirections/insert";
$tbl->btn_add_url_target = "popup"; // "inner" (ברירת מחדל) או "popup"
$tbl->delete = function ($id) {
DB::delete("redirections", ["id" => $id]);
cache_engine::remove("redirections");
};
$tbl->titles = [
["title" => "ID", "field" => "id"],
["title" => "מכתובת", "field" => "url_from"],
["title" => "לכתובת", "field" => "url_to"],
];
$tbl->actions = function ($row) {
return [[
"title" => "עריכה",
"link" => "EDIT",
"target" => "popup",
"icon" => '<i class="fas fa-edit"></i>',
]];
};
return $tbl->get_html();
}
הפרמטר של הבנאי (new panel_table($tblID)) הוא המזהה שהדפדפן שולח בכל בקשה. אם לא תעבירו אותו, הוא נקבע לפי מספר סידורי בבקשה, ולכן ישתנה כשנוספת טבלה נוספת בעמוד. תנו תמיד מזהה ייחודי וקבוע.
order_manager מופעל כברירת מחדל: הרשימה נמיינת לפי עמודת ord, ואם העמודה חסרה בטבלה, המחלקה מריצה ALTER TABLE ... ADD ord INT NOT NULL בזמן ריצה. כבו אותו ($tbl->order_manager = false) בכל רשימה שאין בה גרירה לסידור, וגם כש-query היא מחרוזת ולא אובייקט DB::query.מקורות הנתונים#
| מאפיין | משמעות |
|---|---|
query | DB::query(...) (אובייקט DB_QUERY_OBJECT) או מחרוזת SQL גולמית. המחלקה עוטפת אותה כתת-שאילתה SELECT * FROM (...) AS tbl ומסננת עליה |
rows | מערך שורות ידני שנוסף לראש התוצאות |
rows_func | function($qu, $results) שמחזירה שורות נוספות |
כל שורה חייבת לכלול עמודת id. אתם מגדירים את גודל העמוד ב-limit (ברירת מחדל 50).
עמודות: titles#
titles הוא מערך של עמודות. כל עמודה היא מערך עם המפתחות:
| מפתח | משמעות |
|---|---|
title | כותרת העמודה |
field | שם השדה בשאילתה |
type | id, text (ברירת מחדל), number, date, datetime, select, multiselect, list, image |
func | function($row, $title): מחזירה את הערך להצגה. הפלט מוצג כ-HTML |
link | function($row, $title): מחזירה כתובת, והתא הופך לקישור |
editable | true, או function($row, $title) שמחזירה bool: עריכה בתוך התא |
editable_table | "content" או "conf": לאיזו טבלה נשמר ערך רב-לשוני |
editable_field_table | שם טבלה לעדכון ישיר של השדה |
editable_field_func | function($row, $value, $title): שמירה מותאמת |
editable_success | function($row, $value, $title): נקראת אחרי שמירה מוצלחת |
select_options | מערך [id => טקסט] לעמודות select, multiselect ו-list, למשל DB::query("groups")->get_all("id", "name"). ללא מפתח זה הן נבנות מהערכים השונים שבטבלה |
filter_func | function($fieldObj, $val): מחזירה תנאי SQL (בלי AND בסוף) כשמסננים לפי העמודה |
perms | הרשאה (למשל "export"): עמודה שהמנהל לא מורשה לה מוסרת מהתצוגה |
class, style | מחלקת CSS וסגנון לתא |
confirm | טקסט אישור שהדפדפן מציג לפני שמירת שדה שניתן לעריכה (צד לקוח) |
סוגי עמודה בעלי התנהגות מיוחדת:
date/datetime: מעוצביםY-m-d/Y-m-d H:i.image: ערך מספרי הוא מזהה קובץ ב-STORAGE(מומר ל-URL דרךSTORAGE::get_url), ומחרוזת היא כתובת.list: ערך בפורמט|1||2|(פירוק עםMISC::bgl_explode).select/multiselect: ערך בודד או רשימה.
$tbl->titles = [
["title" => "ID", "field" => "id"],
["title" => "תמונה", "field" => "pic", "type" => "image"],
[
"title" => "כותרת",
"field" => "title",
"link" => fn($row) => CONFIG::$admin_url . "/articles/insert/" . $row["id"],
],
[
"title" => "קטגוריה",
"field" => "cat_id",
"type" => "select",
"select_options" => [1 => "חדשות", 2 => "בלוג"],
],
[
"title" => "סטטוס",
"field" => "published",
"func" => fn($row) => $row["published"] ? "מפורסם" : "טיוטה",
],
["title" => "עדכון", "field" => "updated", "type" => "datetime"],
["title" => "מחיר", "field" => "price", "type" => "number", "editable" => true],
];
הפלט של func מוצג כ-HTML, בלי escape. אם הוא מכיל ערך שמקורו בנתוני משתמש, העבירו אותו דרך htmlspecialchars. בערכים רגילים (בלי func) המחלקה מנקה תווי בקרה ותווים לא תקינים, ומסירה גם אמוג'י, ולכן אל תסתמכו על אמוג'י בתוכן תא.
סינון, חיפוש ומיון#
סינון ומיון מבוצעים בשרת על התת-שאילתה. הדפדפן שולח בכל פעולה גוף JSON עם q (חיפוש), filters (מפתח לכל שדה), ordBy ו-ordDir, pgnm (מספר עמוד).
| סוג עמודה | איך מסננים |
|---|---|
id | שוויון (=) |
text | LIKE %x%, והתאמה מדויקת נשלחת ראשונה |
date, datetime | טווח בין שני תאריכים |
number | מ-עד |
select | שוויון |
multiselect | IN (...) |
list | LIKE '%|id|%' |
| אחר | = אם הערך מספר שלם, אחרת LIKE |
הערך המיוחד --none-- מתאים לשדה ריק או 0. ערכי הסינון עוברים DB::escape.
חיפוש חופשי (q):
- אם הגדרתם
$tbl->searchFunc = function ($escaped_q) { ... }, היא מחזירה את תנאי ה-SQL (הקלט כבר עבר escape). - אחרת החיפוש הוא
LIKEעל כל העמודות של השורה הראשונה בתוצאה.
$tbl->searchFunc = function ($q) {
return "(title LIKE '%$q%' OR body LIKE '%$q%')";
};
מיון ברירת מחדל נקבע ב-default_order (למשל "id DESC"). כש-order_manager פעיל המיון הוא לפי ord.
שם עמודת המיון והכיוון (ordBy, ordDir) שמגיעים מהדפדפן משורשרים לשאילתה ללא ולידציה בליבה הנוכחית. הם נשלחים רק על ידי מנהל מחובר, אבל אל תפעילו panel_table בפאנל שמוגדר skip_permission_check מעל נתונים רגישים.
פעולות לשורה: actions#
actions היא פונקציה שמקבלת שורה ומחזירה מערך של פעולות. כל פעולה:
| מפתח | משמעות |
|---|---|
title | טקסט הכפתור |
icon | HTML של אייקון (FontAwesome 5) |
link | כתובת, או "EDIT" שמשמעה btn_add_url. המזהה מתווסף כ-/id, או ?id= אם בכתובת כבר יש סימן שאלה |
target | popup, _blank, או ברירת מחדל (טעינה בתוך #mainframe דרך adminMovePage) |
func | function($row): פעולה בשרת. אם מוחזרת מחרוזת היא מוצגת למשתמש כהודעה (alertService.fire({html})); מחרוזת שמתחילה ב-<script מורצת |
alert | טקסט אישור לפני הרצה |
perms | הרשאה נדרשת (ADMIN::has_perms) |
class, style | סגנון הכפתור |
action_type | edit או סוג אחר, לעיצוב |
loading | הצגת מחוון טעינה בזמן הרצה |
$tbl->actions = function ($row) {
return [
["title" => "עריכה", "link" => "EDIT", "icon" => '<i class="fas fa-edit"></i>'],
[
"title" => "פרסם",
"icon" => '<i class="fas fa-check"></i>',
"alert" => "לפרסם את המאמר?",
"perms" => "edit",
"func" => function ($row) {
$up = DB::update("articles");
$up->set_var("published", 1);
$up->update((int)$row["id"]);
return "המאמר פורסם";
},
],
];
};
כל פעולה מקבלת טוקן (MISC::encode של הפעולה והמזהה). הדפדפן מחזיר אותו בבקשה, והשרת מריץ רק פעולות שהוגדרו בפועל, לא קוד שרירותי.
פעולת delete (המאפיין $tbl->delete = function ($id, $row) {...}) מוסיפה פעולת מחיקה קבוצתית, ורק למי שיש ADMIN::has_perms("delete"). dblclick_url מוסיף פעולת "כניסה" ומגדיר מה קורה בלחיצה כפולה על שורה.
פעולות קבוצתיות#
$tbl->group_actions זהה במבנה, אבל func היא function($row, $action) שנקראת לכל שורה מסומנת (הדפדפן שולח checkgroup עם רשימת המזהים ואת הטוקן).
$tbl->group_actions = [[
"title" => "הסתר",
"icon" => '<i class="fas fa-eye-slash"></i>',
"alert" => "להסתיר את הפריטים שנבחרו?",
"func" => function ($row, $action) {
$up = DB::update("articles");
$up->set_var("published", 0);
$up->update((int)$row["id"]);
},
]];
ignore_perms = true משמיט את בדיקות ההרשאה בעמודות ובפעולות, אך כרגע get_actions קורסת כשהוא מופעל. אל תשתמשו בו לפני שתבדקו את הרשימה בפועל.עריכה בתוך הטבלה#
עמודה עם editable => true מציגה שדה שנשמר בלחיצה. השמירה נשלחת עם tbl_action = editable_field, והשרת:
- מוודא שהעמודה ניתנת לעריכה לפי הגדרת ה-
titles(לא לפי מה שהדפדפן אומר); - שומר דרך
editable_field_tableאוeditable_field_func, ואם אין, דרךDB::updateלטבלת השאילתה (ל-contentאוconfלפיeditable_table); - קורא ל-
editable_success.
ייצוא לאקסל#
כפתור הייצוא מוצג למנהל עם ADMIN::has_perms("export") ויוצר קובץ xlsx (דרך PhpSpreadsheet, עם כיוון RTL) לפי הסינון הנוכחי. limit בבקשת הייצוא קובע כמה שורות.
בדיקת ההרשאה לייצוא היא רק בצד המסך (הכפתור). בצד השרת אין חסימה לבקשת xls ישירה של מנהל מחובר, ולכן אל תניחו שהסתרת הכפתור מגינה על הנתונים.
ממשק ה-JSON של הטבלה#
הדפדפן שולח ל-URL של הפאנל עצמו גוף JSON, והשרת מחזיר {more_data, lines, paging, order_manager, action_result}. הפרוטוקול המלא מפורט ב-ממשק JSON.
כרטיס פתיחה ותוכן נוסף#
$tbl->intro (טקסט) ו-$tbl->intro_card = true מציגים כרטיס הסבר מעל הטבלה, ו-head_html מקבל מערך אלמנטי HTML לכותרת הטבלה. additional_content היא function($tbl) שמחזירה תוכן נוסף שנשלח עם כל טעינת נתונים.
ראו גם#
- רפרנס panel_table: כל המאפיינים והמתודות
- בניית פאנל חדש
- טפסים: טופס העריכה שהפעולה
EDITפותחת - הרשאות
- ממשק JSON
- עיצוב ורכיבי UI