כתובות ידידותיות (seoUrl)

טבלת seoUrl ממפה כתובת יפה (או ביטוי רגולרי) לנתיב פנימי של קונטרולר. איך מוסיפים שורה, איך ההתאמה עובדת, ומה המטמון עושה.

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

כתובת כמו /about-us או /blog/my-first-post לא חייבת להתאים לשם של קונטרולר. הטבלה CRM_seoUrl מכילה כללי מיפוי: כתובת ידידותית (sysname) והנתיב הפנימי שהיא מייצגת (pageurl), כולל כללים שמבוססים על ביטוי רגולרי. ROUTER::parse_friendly_url בודק את הטבלה לפני שהוא מפרק את הכתובת לקונטרולר ולמתודה.

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

מבנה הטבלה#

הטבלה נקראת seoUrl ב-builder של DB (ו-CRM_seoUrl ב-SQL גולמי):

עמודהמשמעות
idמפתח
sysnameהכתובת הידידותית (ללא / בהתחלה ובסוף), או ביטוי רגולרי עם delimiters כשהדגל regularexp דלוק
pageurlהנתיב הפנימי שאליו הכתובת מתורגמת; יכול לכלול ?key=value ו-$1, $2 בכללי regex
regularexp0 להתאמה מדויקת, 1 להתאמת regex
ordסדר הבדיקה בין כללי ה-regex (עולה); ROUTER::get_seo_tbl() ממיין לפי ord ASC

התאמה מדויקת#

כלל עם regularexp = 0 מתאים כש-sysname שווה לכתובת (אחרי הסרת / מההתחלה ומהסוף), או שווה לכתובת אחרי urldecode. כך כתובת בעברית מותאמת בין אם הדפדפן שלח אותה מקודדת ובין אם לא.

-- כתובת בלי הרחבה: /about-us פותחת את about/index
INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('about-us', 'about', 0, 0);

-- כתובת שמעבירה פרמטר: /products/shoes פותחת products/category?slug=shoes
INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('products/shoes', 'products/category?slug=shoes', 0, 0);

אותו דבר דרך ה-builder (אין DB::insert, ההכנסה היא DB::update(...)->insert()):

$q = DB::update("seoUrl");
$q->set_var("sysname", "about-us");
$q->set_var("pageurl", "about");
$q->set_var("regularexp", 0);
$q->insert();

cache_engine::remove("seoUrl_tbl");   // ראו "מטמון" למטה

אם pageurl כולל ?, מה שאחריו נפרס ונמזג ל-$_GET (ודורס מפתח באותו שם), והנתיב שלפניו הוא זה שמפורק לקונטרולר ולמתודה לפי הטבלה ב-ניתוב.

כללי ביטוי רגולרי#

כלל עם regularexp = 1 מורץ רק אם לא נמצאה התאמה מדויקת. ההתאמה היא preg_match($sysname, $url) על הכתובת, ואם לא הצליחה, שוב על הכתובת אחרי urldecode. ב-pageurl כותבים $1, $2: הקוד מחליף כל $ ב-\ ומריץ preg_replace.

-- /blog/my-first-post  =>  blog/post?slug=my-first-post
INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('/^blog\\/([a-z0-9-]+)$/', 'blog/post?slug=$1', 1, 10);

-- /he/news/12  =>  news/index/12  (id יהיה "12")
INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('/^[a-z]{2}\\/news\\/(\\d+)$/', 'news/index/$1', 1, 20);
עגנו תמיד את הביטוי בשני הצדדים
preg_replace מחליף רק את החלק שהותאם. ביטוי בלי ^ ובלי $ משאיר את שאר הכתובת מוצמד לנתיב הפנימי, ותקבלו נתיב שבור שנראה כמו קונטרולר אחר. פאנל הניהול מזהיר אם חסר ^, אבל לא מונע את השמירה.

ביטויים נבדקים אחד אחד בכל בקשה

כל כללי ה-regex עוברים בלולאה לפי ord, עם preg_match על כל כתובת שלא נפתרה קודם (גם על נכסים שמגיעים ל-PHP). שגיאת קומפילציה ב-preg_replace מושתקת ב-@, כלומר ביטוי שבור פשוט לא נותן תוצאה. שמרו כללים ספציפיים בראש (ord נמוך), והימנעו מביטויים כמו /(.*)/.

כתובת 404 מותאמת#

שורה עם sysname = '404' מגדירה את העמוד שמוצג כשאין התאמה: ה-pageurl שלה הוא הקונטרולר והמתודה שירונדרו. ראו 404, 503 ושגיאות.

INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('404', 'errors/not_found', 0, 0);

ניהול מפאנל הניהול#

הפאנל ADMINMODULE_Seo (system/admin/Seo.php, הכתובת {admin}/Seo) מציג רשימה של כל השורות, ובטופס יש שלושה שדות: כתובת חדשה (sysname), כתובת מקורית (pageurl) ותיבת "ביטוי רגולרי". שמירת כלל regex עוברת בדיקה בצד השרת: חייבים delimiters (/, #, ~, @, % או !), והביטוי חייב להתקמפל ב-preg_match, אחרת השמירה נדחית עם הודעה. הטופס כולל גם תצוגה מקדימה חיה של כתובת דוגמה. שמירה ומחיקה מנקות את המטמון.

שורה לכל רשומה: FormInput_SEO#

טופס רשומה (למשל חדשות) יכול לתת למנהל שדה "Page URL" שמנהל שורה אחת ב-seoUrl לרשומה, באמצעות FormInput_SEO עם pageUrl (ראו SEO למפתחי אתר). השדה מוחק שורה ישנה אם הכתובת השתנתה ומחזיר שגיאה אם הכתובת כבר תפוסה על ידי נתיב אחר. ב-DB::delete של רשומה, מחקו גם את השורה:

$t->delete = function ($id) {
    DB::delete("news", ["id" => $id]);
    ROUTER::remove_by_url("news/item?id=" . $id);   // מוחק שורות seoUrl שה-pageurl שלהן שווה לערך
};

מטמון#

ROUTER::get_seo_tbl() קורא את כל הטבלה ושומר אותה תחת המפתח seoUrl_tbl, 24 שעות כברירת מחדל. הפאנל וה-FormInput_SEO מנקים אותו, אבל הכנסה ישירה ב-SQL או מקוד לא מנקה: קראו ל-cache_engine::remove("seoUrl_tbl") או נקו מטמון מהפאנל (ניקוי ותחזוקה). בנוסף, עמוד מלא שכבר נשמר ב-מטמון עמודים ימשיך להיות מוגש עד שיפוג או יימחק.

קריאה מהקוד#

// נתיב פנימי => כתובת ידידותית (או הנתיב עצמו אם אין שורה)
$friendly = ROUTER::get_by_url("blog/post?slug=my-first-post");

// כתובת ידידותית => נתיב פנימי
$internal = ROUTER::get_by_friendly("about-us");
הערה
get_by_url מחפש התאמה מדויקת הוא משווה את pageurl כמחרוזת, ולכן כללי regex (עם $1) לא יחזירו כתובת ידידותית. לכתובות כאלה בנו את הקישור בעצמכם.

ראו גם#

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