המחלקה SEO (system/collections/SEO.php, api/core/collections/SEO.php בריפו) היא אוסף סטטי ששומר את תגיות ה-<head> של העמוד הנוכחי: כותרת, תיאור, מילות מפתח ו-Open Graph. הקונטרולר ממלא אותה לפני הרינדור, ו-PAGE::output() מדפיס אותה דרך SEO::tags_summary(). הדף הזה הוא הרפרנס של המחלקה; מה מפתח אתר צריך לעשות סביבה (canonical, robots, sitemaps, 404, JSON-LD) מופיע ב-SEO למפתחי אתר, והתגיות מנקודת מבט של העמוד ב-תגיות SEO ו-Schema.
חבילת ה-SEO של ה-Market (סורק, ממצאים, IndexNow) היא מנוע נפרד ומתוארת ב-חבילת ה-SEO. המחלקה SEO היא רק שכבת התגיות של הליבה.
מחזור החיים בבקשה אחת#
PAGE::load()קוראת ל-SEO::init()מיד אחריPARAMS::init(): כל השדות מתאפסים למחרוזת ריקה.- הקונטרולר קורא ל-
SEO::set()/SEO::set_var()ול-SEO::set_key(). PAGE::output()(רק בעמוד מלא, כלומר לאpmode=empg/inner) קוראת ל-SEO::tags_summary()בתחילת ה-<head>ומדפיסה את התוצאה, ובעמוד לקוח גםSEO::get_schema("WebPage").
משתנים סטטיים#
| משתנה | ברירת מחדל | משמעות |
|---|---|---|
SEO::$fields | מערך עם og_url, meta_title, meta_description, meta_keywords, og_title, og_description, og_image (כולם "") | הערכים שיודפסו. מפתח חדש שמוסיפים דרך set_var נכנס למערך ומודפס אף הוא |
SEO::$title_include_sitename | true | האם להוסיף ל-<title> את " - " ואחריו CONFIG::$platform_data["seo_title"] |
SEO::$page_key | "" | מפתח לחיפוש ב-CRM_seo_center עם is_by_url = 0 |
init() לא מאפסת את שני המשתנים האחרים
SEO::init() מאפסת רק את ערכי $fields. $title_include_sitename ו-$page_key נשארים כפי שהוגדרו, אבל מכיוון שכל בקשה היא תהליך PHP חדש, זה משנה רק בהרצות ארוכות (CLI, cron).SEO::init()#
static function init()
מאפסת כל ערך ב-SEO::$fields ל-"" (גם מפתחות שנוספו בזמן ריצה). נקראת על ידי PAGE::load(); אין סיבה לקרוא לה מקוד אתר.
SEO::set_key()#
static function set_key($key)
מציבה את SEO::$page_key. המפתח הוא כינוי לרשומה ב-CRM_seo_center עם is_by_url = 0 (למשל home או news_42), לפי מה שנכנס בשדה FormInput_SEO::$key בפאנל. אין מחזיר.
SEO::set(), SEO::set_var()#
static function set($fields, $value) // כינוי: return self::set_var($fields, $value)
static function set_var($field, $value) // אין ערך החזרה
שני הפרמטרים נקראים $fields / $field אבל מדובר בשדה אחד. הערך מועבר דרך המתג הבא (המפתחות הם שמות "קצרים", וחלקם כותבים לשני שדות):
$field | נכתב אל | עיבוד הערך |
|---|---|---|
og_url, url | og_url | ללא עיבוד |
title | meta_title ו-og_title | strip_tags |
description | meta_description ו-og_description | strip_tags, כיווץ רצפי רווחים לרווח אחד, trim |
keywords | meta_keywords | strip_tags |
og_image, image | og_image | מספר שלם (MISC::is_int): STORAGE::get_thumb($value, 1200, 627). אחרת כתובת כפי שהיא |
| כל מפתח אחר | $fields[$field] | ללא עיבוד: meta_title / meta_description / meta_keywords / og_title / og_description ישירות, og_type, author ועוד |
// application/controllers/news.php
SEO::set("title", $row["title"]);
SEO::set("description", $row["summary"]);
SEO::set("image", (int)$row["image_id"]); // מספר: נהפך לתמונה 1200x627
SEO::set("url", CONFIG::$site_url . "news/" . (int)$row["id"]);
SEO::set("og_type", "article"); // מודפס כ-og:type
SEO::set_key("news_" . (int)$row["id"]); // רשומת seo_center לפי מפתח
כל מפתח שלא מוכר נכנס ל-$fields ובסוף tags_summary מודפס כ-<meta name="<key>" content="..."> (קידומת meta_ נחתכת). שגיאת כתיבה (SEO::set("descripton", ...)) לא תיכשל, אלא תדפיס תגית descripton. גם עמודה לא צפויה בטבלת seo_center מגיעה לראש העמוד באותה דרך (ראו למטה).
SEO::tags_summary()#
static function tags_summary() // מחזירה string של HTML
נקראת על ידי PAGE בלבד. הסדר:
- ברירות מחדל מהפלטפורמה. שדה ריק מקבל את
seo_title,seo_descriptionאוseo_keywordsמ-CONFIG::$platform_data. אם אין כותרת,SEO::$title_include_sitenameמכובה כדי שהשם לא יודפס פעמיים. - חיפוש ב-
CRM_seo_center. המפתח הראשון הוא הנתיב:og_url(אוREQUEST_URIכשהוא ריק), רק החלק של ה-path, בלי/בהתחלה ובסוף, אחריurldecode, עםis_by_url = 1. אם לא נמצא וגםSEO::$page_keyלא ריק: לפיpage_keyעםis_by_url = 0. - דריסה מהרשומה. כל עמודה לא ריקה ברשומה עוברת
set_varודורסת את מה שהקונטרולר קבע. עמודות מבניות מדולגות (id,page_key,is_by_url,ord,title_include_sitename,platform_id,lang,is_active,date_created,date_updated,created,updated), ו-title_include_sitenameנקרא רק כשישmeta_titleברשומה.scriptsמוזרק ל-PAGE::add_meta().og_image/imageברשומה הוא מספר קובץ (> 0) ונפתר ל-STORAGE::get_by_name($val, "wide", 1200, 627). - הדפסה. קודם
<meta name="twitter:card" content="summary_large_image">, אחריה תגית האימות של Search Console (ראוSeoVerification), ואז כל שדה לא ריק לפי הטבלה:
| שדה | פלט |
|---|---|
meta_title | <title> עם הערך הגולמי (" - " . seo_title נוסף כש-$title_include_sitename) |
og_title | og:title ו-twitter:title |
og_description | og:description ו-twitter:description |
og_image | og:image ו-twitter:image |
og_type | og:type ו-twitter:type |
og_url | og:url ו-twitter:url |
| כל שדה אחר | <meta name="<key ללא meta_>" content="...">, כלומר meta_description מודפס כ-<meta name="description"> |
כל הערכים חוץ מ-<title> עוברים htmlspecialchars.
(1) <title> מודפס בלי htmlspecialchars (רק strip_tags כשהוגדר דרך set_var), ולכן & בכותרת לא מקודד. (2) og_url מודפס רק כשהוגדר במפורש: הליבה לא ממלאת אותו מ-REQUEST_URI. (3) החיפוש ב-seo_center בלי platform_id ו-lang: באתר רב-פלטפורמות אותו נתיב מקבל אותו meta בכל הדומיינים. (4) 1 עד 2 שאילתות seo_center בכל רינדור, ללא מטמון. (5) כל עמודה לא מוכרת בטבלה נשפכת כ-<meta name=...>, לכן אל תוסיפו עמודות עזר ל-seo_center בלי להוסיף אותן לרשימת הדילוג.
SEO::get_schema()#
static function get_schema($type) // "WebPage" | "Organization" | "NewsArticle"
מחזירה מחרוזת <script type="application/ld+json">...</script>. סוג לא מוכר מחזיר תגית עם [].
$type | מה נבנה |
|---|---|
WebPage | @context, @type: WebPage, @id = CONFIG::$site_url. זו הקריאה היחידה שהליבה עושה בעצמה, בכל עמוד לקוח |
Organization | name = seo_title של הפלטפורמה, url, ו-logo קבוע |
NewsArticle | headline = meta_title הנוכחי, ושדות image, author, datePublished, dateModified, publisher.logo קבועים |
שני הסוגים מדפיסים לוגו מ-rcs.mako.co.il, מחבר "משה משה" (בשדה url של Person) ותאריכים קבועים 2021-12-12. אל תקראו להם באתר חי: זה structured data שקרי. בנו JSON-LD בעצמכם והזריקו אותו עם PAGE::add_meta() (דוגמה מלאה ב-SEO למפתחי אתר). רק WebPage בטוח לשימוש.
החלקים הסמוכים#
PAGE::add_meta(), print_meta_tags()#
public static function add_meta($val, $area = "head")
public static function print_meta_tags($area = "")
add_meta מוסיפה מחרוזת HTML גולמית (תגית <meta>, <link>, <script>) לאזור (head כברירת מחדל). print_meta_tags מדפיסה לפי אזור. שימו לב: ערך שמוזרק כך אינו עובר escaping, ולכן כל נתון שמגיע ממשתמש חייב לעבור htmlspecialchars לפני ההזרקה. פרטים ב-PAGE: נכסים, מטא ואירועים.
PAGE::add_meta('<link rel="canonical" href="' . htmlspecialchars(CONFIG::$site_url . "news/" . (int)$row["id"], ENT_QUOTES) . '">');
PAGE::$robots#
מחרוזת, ברירת מחדל "ALL", מודפסת בכל עמוד ב-<meta name="ROBOTS" content="..."> מיד אחרי תגיות head:
PAGE::$robots = "noindex, follow";
SeoVerification::meta_tag()#
public static function meta_tag(): string // system/libraries/SeoVerification.php
מחזירה <meta name="google-site-verification" content="..."> מהטוקן ששמור בפרמטר של CRM_params, או מחרוזת ריקה כשאין טוקן. tags_summary קוראת לה בתוך try/catch, בכל עמוד ולא רק בדף הבית, ושגיאה בה לא פוגעת בעמוד. הטוקן נקרא ב-SELECT ישיר מהטבלה, לא דרך PARAMS, כך שהוא נכנס לתוקף מיד אחרי שמירה (אבל עמוד שנשמר במטמון לא יציג אותו, ראו מטמון עמודים מלא).
רשומות seo_center#
הטבלה CRM_seo_center נערכת בפאנל /{admin}/seo_center ובשדה FormInput_SEO. עמודות הליבה:
| עמודה | סוג | משמעות |
|---|---|---|
id | INT PK | |
page_key | VARCHAR(255) | הנתיב (כש-is_by_url = 1) או המפתח שהוגדר ב-SEO::set_key |
is_by_url | TINYINT(1) | 1 מחפש לפי נתיב הבקשה, 0 לפי SEO::$page_key |
meta_title, title_include_sitename | VARCHAR(255), TINYINT(1) | כותרת, והאם להוסיף שם אתר |
meta_description, meta_keywords | TEXT | |
og_title, og_description | VARCHAR(255), TEXT | |
og_image | INT | מזהה קובץ בטבלה storage |
ord | INT | סדר בפאנל |
אתר שמקים מסד חדש חייב ליצור אותה (DDL ב-SQL להקמה). ראו גם סכמת הטבלאות.
// הוספת רשומת SEO לדף הבית בלי הפאנל (לא להריץ על אתר חי בלי לבדוק קודם שאין רשומה)
$ins = DB::update("seo_center");
$ins->set_var("page_key", "home");
$ins->set_var("is_by_url", 0);
$ins->set_var("meta_title", "Wizzo - הבית");
$ins->set_var("meta_description", "תיאור קצר לדף הבית");
$ins->insert();
דף NEWPAGE#
הקונטרולר newpage_public (דפי נחיתה מ-NEWPAGE) משתמש ב-SEO::set_var("title", ...) וב-SEO::set_var("description", ...) מנתוני head של השירות. את og:image ואת noindex הוא מוסיף ישירות ב-PAGE::add_meta(), לא דרך SEO::$fields.
ראו גם#
- SEO למפתחי אתר: canonical, robots, 404 מול 503, sitemap, JSON-LD
- תגיות SEO ו-Schema, PAGE: נכסים, מטא ואירועים, PAGE (רפרנס)
- כתובות ידידותיות ו-הפניות 301
- חבילת ה-SEO, IndexNow ואימות