مقالات

راهنمای کامل تزریق داینامیک اسکیمای Article (JSON-LD) در قالب وبلاگ

راهنمای کامل تزریق داینامیک اسکیمای Article (JSON-LD) در قالب وبلاگ

سلام! نگینم. بیا یه گپ خودمونی بزنیم در مورد اون ستاره‌های خوشگل طلایی، سوالات متداول و قیمت‌هایی که زیر لینک‌ها تو گوگل می‌بینیم.

همه‌مون می‌دونیم پیاده‌سازی داده‌های ساختاریافته (Structured Data) چقدر برای جلب توجه کاربر حیاتیه، ولی «چطوری» انجام دادنش، اصل ماجراست.

من خودم روزگاری دونه دونه کدهای JSON رو برای محصولات کپی می‌کردم و با هر تغییر قیمت یا موجودی انبار، نصف روز درگیر آپدیت دستی اون کدها بودم. یه کابوس واقعی! تا اینکه فهمیدم راه بهتری هم هست: «تزریق داینامیک».

تو این مقاله نمی‌خوام تئوری‌های خشک و حوصله‌سربر بگم؛ می‌خوام دقیقاً بهت نشون بدم چطور یک بار برای همیشه این سیستم هوشمند رو راه‌اندازی کنی تا خودش اتوماتیک همه‌چی رو برای گوگل آپدیت کنه. چه تو هم مثل من اهل کدنویسی با PHP باشی، چه انعطاف‌پذیری گوگل تگ منیجر رو ترجیح بدی.

آماده‌ای از شر این همه کار تکراری خلاص شیم و یه قدم حرفه‌ای‌تر عمل کنیم؟

جدول مقایسه: چرا روش داینامیک برنده است؟

ویژگی روش دستی / پلاگین (سنتی) روش تزریق داینامیک (PHP/GTM)
دقت اطلاعات پایین (همیشه وابسته به آپدیت دستی) ۱۰۰٪ دقیق (همگام‌سازی لحظه‌ای با دیتابیس)
هزینه نگهداری بسیار بالا (کار تکراری، زمان‌بر و پرهزینه) تقریباً صفر (فقط یک بار راه‌اندازی)
پوشش فیلدها محدود به امکانات استاندارد پلاگین کامل (پشتیبانی نامحدود از فیلدهای سفارشی)
ریسک خطا بالا (خطای انسانی، فراموشی، شکستن JSON) بسیار پایین (سیستماتیک و قابل کنترل)
بهترین برای سایت‌های خیلی کوچک و وبلاگ‌های شخصی فروشگاه‌ها، سایت‌های بزرگ و بیزینس‌های جدی

چرا اسکیمای دستی (Manual) یا پلاگین‌ها کافی نیستند؟ (مزایای تزریق داینامیک)

راستش رو بخوای، اون راه‌حل‌های دم‌دستی (چه پلاگین، چه دستی) یه جورایی مثل چسب‌زخم زدن به یه مشکل عمیق‌ترن. قشنگن، ولی موقتی‌ان و دقیق نیستن.

وقتی سایتت کوچیکه و مثلاً ۱۰ تا مقاله داری، شاید کارت راه بیفته. اما امان از روزی که سایتت بزرگ بشه، محصولاتت زیاد بشه، یا بخوای یه چیز «خاص» و «سفارشی» رو به گوگل نشون بدی که توی اون پلاگین پیش‌بینی نشده.

اونجاست که می‌فهمی چرا تزریق داینامیک اسکیما (Dynamic Schema Injection) فقط یه گزینه لوکس نیست، بلکه یه ضرورت مطلقه.

محدودیت‌های پلاگین‌های عمومی (عدم پوشش فیلدهای سفارشی و ایجاد کد اضافه)

بریم سراغ مشکل اول: پلاگین‌ها.

ببین، پلاگین‌های سئو مثل یه دست لباس آماده (Off-the-rack) می‌مونن. برای یه کاربر معمولی خوبن، اما دقیقاً فیتِ تنِ بیزینس «تو» نیستن. مشکل اصلی اینجاست:

۱. فیلدهای سفارشی (Custom Fields) رو نمی‌بینن: این پلاگین‌ها یه سری فیلد استاندارد دارن. مثلاً برای اسکیمای محصول، قیمت و موجودی رو ازت می‌گیرن. حالا فرض کن تو یه سایت فروش قهوه داری و یه فیلد سفارشی ساختی به اسم “میزان کافئین” یا “نوع دَم‌آوری”. پلاگین اصلاً اینا رو نمی‌بینه و نمی‌تونه توی اسکیما بذاره! تو یه دیتای ارزشمند داری که نمی‌تونی به گوگل نشونش بدی.

۲. کد اضافه و گاهی اشتباه (Bloat): خیلی وقت‌ها این پلاگین‌ها کدهای اضافه‌، تکراری و گاهی حتی اشتباه به هدر سایتت اضافه می‌کنن. این هم می‌تونه سرعت رو یه کم بگیره و هم گوگل رو گیج کنه. بدتر از همه اینه که تو معمولاً کنترل ۱۰۰ درصدی روی خروجی نهایی کد نداری و این برای منی که روی جزئیات سئو حساسم، اصلاً قابل قبول نیست.

کابوس نگهداری دستی: چگونه یک تغییر کوچک، ده‌ها ساعت کار می‌سازد

حالا فرض کنیم گفتی «ول کن پلاگینو، خودم دستی می‌نویسم! هر صفحه‌ای اسکیمای خودشو داره.» اینجاست که کابوس واقعی شروع می‌شه.

بذار یه خاطره واقعی برات تعریف کنم: یه بار برای یه سایت فروشگاهی بزرگ، اسکیمای محصولات رو دستی پیاده کردیم. صدها محصول بود. همه‌چی عالی بود تا اینکه تیم مارکتینگ تصمیم گرفت یه فیلد جدید به اسم “گارانتی ویژه ۲ ساله” به همه‌ی محصولات اضافه کنه.

می‌دونی معنی این چی بود؟ من و تیمم مجبور شدیم دونه به دونه کدهای JSON-LD رو برای صدها محصول باز کنیم، اون خط کد جدید رو اضافه کنیم و آپلود کنیم. یه تغییر کوچیک، تبدیل شد به چند روز کار تکراری، خسته‌کننده و پر از استرس (که نکنه یه ویرگول جا بندازیم و کل کد خراب بشه).

حالا فرض کن قیمت یه محصول توی سایتت عوض بشه و تو یادت بره اسکیمای دستی رو هم آپدیت کنی… اطلاعاتی که کاربر تو صفحه می‌بینه (مثلاً ۱۰۰ هزار تومن) با اطلاعاتی که اسکیما به گوگل می‌ده (مثلاً ۹۰ هزار تومن) نمی‌خونه. این عدم تطابق، اعتمادی که گوگل به سختی به سایتت کرده رو نابود می‌کنه.

نگهداری دستی یعنی تو همیشه از تغییرات عقبی.

مزایای کلیدی روش داینامیک: دقت ۱۰۰٪، نگهداری صفر و همگام‌سازی آنی با محتوا

حالا بیایم سراغ راه نجات: تزریق داینامیک.

این روش یعنی چی؟ خیلی ساده‌ست. یعنی ما به جای کپی/پیست کردن کد، یه الگو (Template) هوشمند طراحی می‌کنیم. به وردپرس (یا هر سیستم دیگه‌ای) می‌گیم:

«ببین، هر وقت یه صفحه محصول باز شد، خودت برو اسم محصول رو از عنوان بردار، قیمتش رو از فیلد قیمت ووکامرس بخون، و اون “میزان کافئین” سفارشی رو هم از اون کاستوم فیلدی که ساختم بردار و بذار توی این الگوی اسکیما.»

مزایاش فوق‌العاده‌ست:

  • دقت ۱۰۰٪: چون کد اسکیما داره اطلاعات رو مستقیماً از همون دیتابیسی می‌خونه که کاربر تو صفحه می‌بینه، امکان نداره اطلاعاتت قدیمی یا اشتباه باشه.
  • نگهداری صفر: این قشنگ‌ترین قسمتشه. تو یه بار الگو رو می‌سازی و تمام. دیگه مهم نیست ۱۰۰ تا محصول داری یا ۱۰ هزار تا. قیمت رو عوض کنی؟ همون لحظه تو اسکیما هم عوض می‌شه. محصول جدید اضافه کنی؟ اتوماتیک اسکیمای کامل و دقیقش براش ساخته می‌شه. دیگه خبری از کار دستی نیست.
  • همگام‌سازی آنی: دیگه فاصله زمانی بین آپدیت محتوا و آپدیت اسکیما وجود نداره. همه‌چی در لحظه اتفاق میفته و گوگل همیشه جدیدترین اطلاعات تو رو داره.

خب، رسیدیم به بخش جذاب ماجرا. قبل از اینکه بخوایم چیزی رو «تزریق» کنیم، اول باید اون «چیز» رو بسازیم، درسته؟

فکر کن می‌خوای یه قالب کیک سفارشی درست کنی. اول باید طرح اولیه‌اش رو روی کاغذ بیاری. این گام صفر، دقیقاً همون کار رو می‌کنه. ما یه «قالب» یا «تمپلیت» از اسکیمایی که می‌خوایم می‌سازیم.

گام صفر: آماده‌سازی قالب (Template) اسکیمای Article با متغیرها

اینجا جاییه که ما به سیستم می‌گیم: «ببین، من یه همچین ساختاری می‌خوام، اما یه سری جاهاش خالیه. اون جاهای خالی رو بعداً خودت باید به صورت اتوماتیک پُر کنی.» این «جاهای خالی» همون متغیرهای ما هستن.

(کد نمونه) ساختار پایه JSON-LD برای Article یا BlogPosting

بیا با هم یه اسکلت تمیز و استاندارد برای BlogPosting (که نوع خاصی از Article هست و برای وبلاگ‌ها عالیه) بسازیم. این کد JSON-LD ماست:

JSON
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "%%POST_TITLE%%",
  "datePublished": "%%POST_PUBLISH_DATE%%",
  "dateModified": "%%POST_MODIFIED_DATE%%",
  "author": {
    "@type": "Person",
    "name": "%%AUTHOR_NAME%%"
  },
  "publisher": {
    "@type": "Organization",
    "name": "وزیر سئو",
    "logo": {
      "@type": "ImageObject",
      "url": "https://vazirseo.com/logo.png"
    }
  },
  "image": {
    "@type": "ImageObject",
    "url": "%%FEATURED_IMAGE_URL%%",
    "width": "%%IMAGE_WIDTH%%",
    "height": "%%IMAGE_HEIGHT%%"
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "%%POST_URL%%"
  },
  "description": "%%POST_EXCERPT%%"
}

یه نکته دوستانه: بخش publisher (ناشر) معمولاً ثابته. من اینجا اسم «وزیر سئو» رو به عنوان مثال گذاشتم. تو باید اسم برند و آدرس لوگوی خودت رو اونجا بذاری. این بخش معمولاً متغیر نیست و در همه‌ی مقاله‌ها یکسانه.

شناسایی متغیرهای (Placeholders) مورد نیاز ما

دیدی اون کلماتی که دورشون %%...%% گذاشتم؟ این‌ها همون «متغیرها» یا «جاهای خالی» (Placeholders) ما هستن.

این‌ها به سیستم ما می‌گن: «اینجا واینستا! برو بگرد و دیتای واقعی رو پیدا کن و بذار جای من.» این متغیرها زبان مشترک بین «قالب» ما و «دیتابیس» وردپرس ما می‌شن. ما خودمون این اسم‌ها رو انتخاب می‌کنیم. مهم اینه که اسمی که انتخاب می‌کنی یادت بمونه تا در مرحله بعد ازش استفاده کنی.

مثال: %%POST_TITLE%%, %%POST_DATE%%, %%AUTHOR_NAME%%, %%FEATURED_IMAGE%%

بیا با هم متغیرهایی که تو کد بالا استفاده کردم رو معنی کنیم:

  • %%POST_TITLE%%: این همون عنوان مقاله‌ست (H1).
  • %%POST_PUBLISH_DATE%%: تاریخی که مقاله منتشر شده.
  • %%POST_MODIFIED_DATE%%: تاریخی که مقاله آخرین بار آپدیت شده (این برای گوگل خیلی مهمه!).
  • %%AUTHOR_NAME%%: اسم نویسنده‌ی اون پست خاص.
  • %%FEATURED_IMAGE_URL%%: آدرس کامل (URL) عکس شاخص مقاله.
  • %%IMAGE_WIDTH%% و %%IMAGE_HEIGHT%%: طول و عرض عکس شاخص (اینم برای گوگل خیلی خوبه).
  • %%POST_URL%%: آدرس صفحه‌ی همین مقاله‌ای که کاربر توشه.
  • %%POST_EXCERPT%%: خلاصه مقاله (یا همون متا دیسکریپشن).

خب، حالا ما یه «قالب» بی‌نقص و آماده داریم. این اسکلت کار ماست. تو گام بعدی، باید به وردپرس یاد بدیم چطوری این جاهای خالی رو با اطلاعات واقعی هر صفحه پُر کنه.

آماده‌ای بریم سراغ اینکه چطور این متغیرها رو «زنده» کنیم؟

این روش «تخصصی» ماجراست. راستش رو بخوای، این همون روشیه که من خودم عاشقشم، چون کنترل کامل و مطلق رو بهمون می‌ده. دیگه خبری از تنظیمات پلاگین و کدهای اضافه نیست؛ فقط «تو»یی و «کد».

روش اول (تخصصی): تزریق داینامیک در وردپرس با PHP (بدون پلاگین)

قراره مستقیم بریم سراغ قلب وردپرس و بهش بگیم دقیقاً چی‌کار کنه. نترس! مثل یه دستور پخت دنبالش می‌کنیم.

استراتژی: استفاده از هوک wp_head در فایل functions.php

هر قالب وردپرس یه فایل جادویی به اسم functions.php داره. این فایل یه جورایی مثل «مرکز فرماندهی» قالب توئه.

استراتژی ما اینه:

  1. یه تابع (Function) با PHP می‌نویسیم که کارش ساختن اسکیمای داینامیک باشه.
  2. از یه «قلاب» (Hook) به اسم wp_head استفاده می‌کنیم.
  3. این هوک به وردپرس می‌گه: «هی وردپرس! درست قبل از اینکه می‌خوای تگ <head> رو تو HTML صفحه ببندی، یه لحظه وایسا و این تابع (Function) من رو اجرا کن.»

این بهترین و تمیزترین جا برای اضافه کردن کد JSON-LD ماست.

(کد نمونه) ایجاد تابع PHP برای فراخوانی اطلاعات پست (Post Data)

قبل از اینکه کد کامل رو ببینی، بیا اول ببینیم چطور می‌تونیم اون متغیرها (%%...%%) که تو گام صفر ساختیم رو با دیتای واقعی پُر کنیم.

آموزش گام به گام: دریافت عنوان، تاریخ انتشار و تاریخ به‌روزرسانی (get_the_title, get_the_date)

وردپرس برای هر چیزی که فکرش رو بکنی یه تابع آماده داره:

  • برای عنوان (%%POST_TITLE%%): از تابع get_the_title() استفاده می‌کنیم.
  • برای تاریخ انتشار (%%POST_PUBLISH_DATE%%): اینجا یه نکته حرفه‌ای داره. اسکیما (و گوگل) عاشق فرمت تاریخ استاندارد (ISO 8601) هستن. پس ما به جای get_the_date() خالی، از get_the_date('c') استفاده می‌کنیم. اون 'c' کوچولو همه‌چی رو استاندارد می‌کنه.
  • برای تاریخ به‌روزرسانی (%%POST_MODIFIED_DATE%%): دقیقاً مثل قبلی، از get_the_modified_date('c') استفاده می‌کنیم. این برای E-E-A-T حیاتیه چون به گوگل نشون می‌ده محتوای تو «تازه» و آپدیت‌شده‌ست.

آموزش گام به گام: دریافت URL و ابعاد تصویر شاخص (get_the_post_thumbnail_url)

برای عکس شاخص، ما فقط URL رو نمی‌خوایم؛ گوگل عاشق اینه که «ابعاد» عکس رو هم بدونه.

  • برای URL و ابعاد (%%FEATURED_IMAGE_...%%): بهترین تابع wp_get_attachment_image_src() هست. این تابع یه آرایه خوشگل بهمون برمی‌گردونه که توش هم URL عکس هست، هم عرض (width) و هم ارتفاع (height).

(کد نمونه) نحوه ساخت آرایه PHP و تبدیل آن به JSON-LD با json_encode

خب، وقتشه همه‌ی اینا رو بذاریم کنار هم. این کد کامل رو باید کپی کنی و بذاری آخر فایل functions.php قالبت:

PHP
function vazir_seo_inject_article_schema() {
    
    // گام 1: شرط می‌ذاریم که این کد فقط برای مقاله‌ها اجرا بشه
    if ( is_single() ) {
        
        // گام 2: اطلاعات اصلی پست رو می‌گیریم
        global $post; // دسترسی به آبجکت پست
        $post_id = $post->ID;
        
        // گام 3: اطلاعات عکس شاخص رو کامل می‌گیریم
        $image_data = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'full' );
        $image_url = $image_data[0] ?? ''; // URL
        $image_width = $image_data[1] ?? '800'; // Width (یه پیش‌فرض می‌ذاریم)
        $image_height = $image_data[2] ?? '600'; // Height (یه پیش‌فرض می‌ذاریم)

        // گام 4: ساختار آرایه PHP رو بر اساس قالب گام صفر می‌سازیم
        $schema_array = [
            "@context" => "https://schema.org",
            "@type" => "BlogPosting",
            "mainEntityOfPage" => [
                "@type" => "WebPage",
                "@id" => get_permalink( $post_id ) // %%POST_URL%%
            ],
            "headline" => get_the_title( $post_id ), // %%POST_TITLE%%
            "datePublished" => get_the_date( 'c', $post_id ), // %%POST_PUBLISH_DATE%%
            "dateModified" => get_the_modified_date( 'c', $post_id ), // %%POST_MODIFIED_DATE%%
            
            // بخش حیاتی نویسنده (E-E-A-T)
            "author" => [
                "@type" => "Person",
                "name" => get_the_author_meta( 'display_name', $post->post_author ) // %%AUTHOR_NAME%%
                // "url" => get_author_posts_url( $post->post_author ) // (اختیاری ولی عالی)
            ],
            
            // بخش حیاتی ناشر (E-E-A-T)
            "publisher" => [
                "@type" => "Organization",
                "name" => "وزیر سئو", // اسم سایت یا برند شما
                "logo" => [
                    "@type" => "ImageObject",
                    "url" => "https://vazirseo.com/logo.png" // آدرس لوگوی شما
                ]
            ],
            
            "image" => [
                "@type" => "ImageObject",
                "url" => $image_url, // %%FEATURED_IMAGE_URL%%
                "width" => $image_width, // %%IMAGE_WIDTH%%
                "height" => $image_height // %%IMAGE_HEIGHT%%
            ],
            "description" => get_the_excerpt( $post_id ) // %%POST_EXCERPT%%
        ];
        
        // گام 5: آرایه PHP رو به JSON تبدیل می‌کنیم و چاپ می‌کنیم
        echo '<script type="application/ld+json">' . 
             json_encode( $schema_array, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT ) . 
             '</script>' . "\n";
    }
}

// گام 6: تابع رو به هوک wp_head وصل می‌کنیم
add_action( 'wp_head', 'vazir_seo_inject_article_schema' );

نکته حیاتی (E-E-A-T): فراخوانی داینامیک اطلاعات نویسنده (Person) و ناشر (Organization)

دیدی تو کد بالا چی‌کار کردیم؟ این خیلی مهمه.

  1. نویسنده (Person): ما اسم نویسنده رو دستی ننوشتیم. با get_the_author_meta('display_name', ...) اسم نویسنده رو مستقیم از پروفایل وردپرسش خوندیم. این یعنی اگه نویسنده اسمتو تو پروفایلش عوض کنه، اسکیما هم همون لحظه آپدیت می‌شه. این یعنی «اعتبار» (Authority) و «تخصص» (Expertise).
  2. ناشر (Organization): بخش ناشر معمولاً ثابته و به «برند» یا «سایت» تو اشاره داره. من اون رو به عنوان مثال «وزیر سئو» گذاشتم. تو باید اسم و آدرس لوگوی سایت خودت رو بذاری. این به گوگل کمک می‌کنه بفهمه این محتوا مال کدوم «موجودیت» (Entity) معتبره.

شرط بارگذاری: چگونه این کد را فقط در صفحات is_single() (مقالات) اجرا کنیم؟

مهم‌ترین بخش اون کد، خط اول تابع بود: if ( is_single() ).

این یه شرط ساده‌ست که به وردپرس می‌گه: «ببین، کدهای بعدی رو فقط و فقط اگه کاربر توی یه صفحه “پست تکی” (Single Post) یا همون “مقاله” بود اجرا کن.»

چرا این مهمه؟ چون ما نمی‌خوایم اسکیمای Article تو صفحه اصلی سایت، صفحه دسته‌بندی یا صفحه «درباره ما» لود بشه! این کار هم جلوی کدهای اضافه و هم خطاهای اسکیما رو می‌گیره و باعث می‌شه اسکیمای ما فقط جایی که باید باشه، لود بشه.

می‌ریم سراغ روش مدرن و به نظر من، خیلی همه‌کاره و جذاب: گوگل تگ منیجر (GTM).

اگه functions.php مثل جراحی قلب باز بود، GTM مثل جراحی لاپاراسکوپیه. ما مستقیم کد سایت رو دستکاری نمی‌کنیم، بلکه از یه «واسط» هوشمند (همون تگ منیجر) استفاده می‌کنیم تا کدها رو به صفحه‌مون «تزریق» کنه.

روش دوم (مدرن): تزریق داینامیک با Google Tag Manager (GTM)

این روش برای کیا خوبه؟ برای کسایی که:

۱. به کد functions.php دسترسی ندارن (مثلاً تیم مارکتینگ هستن نه برنامه‌نویس).

۲. نمی‌خوان با هر تغییری منتظر تیم فنی بمونن و استقلال عمل می‌خوان.

۳. می‌خوان همه‌چیز رو از یه داشبورد مرکزی مدیریت کنن.

این روش چگونه کار می‌کند؟ (استفاده از Data Layer یا DOM Scraping)

خب، GTM چطوری می‌فهمه عنوان مقاله یا اسم نویسنده چیه؟ دو تا راه داره:

۱. Data Layer (روش حرفه‌ای و امن): این بهترین روشه. تو (یا برنامه‌نویس‌ات) یه بار اطلاعات کلیدی صفحه (مثل عنوان، نویسنده، تاریخ) رو توی یه لایه اطلاعاتی به اسم dataLayer می‌ذاری. GTM مستقیم از اونجا می‌خونه. اطلاعات تمیز و دقیق دستشه.

۲. DOM Scraping (روش سریع ولی شکننده): این روش یعنی به GTM می‌گیم: «برو تو صفحه بگرد، مثلاً دنبال اولین تگ <h1> بگرد و متنش رو به عنوان “عنوان” بردار» یا «برو دنبال کلاسی به اسم .author-name بگرد و اسم نویسنده رو از توش بکش بیرون.»

  • چرا شکننده‌ست؟ چون اگه فردا طراح سایت بیاد و کلاس author-name رو بکنه .post-author، تگ منیجر تو دیگه نمی‌تونه اسم نویسنده رو پیدا کنه و اسکیمات ناقص می‌شه!

ما تو این آموزش فرض رو بر روش ساده‌تر یعنی استفاده از متغیرهای داخلی GTM و یه کم DOM Scraping می‌ذاریم.

پیش‌نیاز: فعال‌سازی متغیرهای داخلی (Built-in Variables) در GTM

قبل از هر کاری، باید بری تو اکانت GTM، بخش Variables.

اونجا یه دکمه Configure برای Built-in Variables (متغیرهای داخلی) هست. مطمئن شو که حداقل اینا تیک خورده باشن:

  • Pages: Page URL, Page Path, Page Hostname
  • Utilities: Event
  • Clicks: (فعلاً لازم نداریم ولی فعال بودنشون خوبه)
  • Forms: (فعلاً لازم نداریم)

اینا ابزارهای اولیه‌ی ما هستن. مثلاً {{Page Path}} به ما می‌گه تو کدوم آدرس صفحه هستیم تا بتونیم تریگر (Trigger) رو درست تنظیم کنیم.

(آموزش گام به گام) ساخت متغیرها برای استخراج عنوان، نویسنده و تاریخ از صفحه

خب، متغیرهای داخلی کافی نیستن. ما باید متغیرهای «سفارشی» (User-Defined Variables) خودمون رو بسازیم تا اطلاعات خاص مقاله رو از صفحه بکشیم بیرون.

برو به بخش Variables > User-Defined Variables > New:

۱. متغیر عنوان:

  • اسم متغیر: DOM - Page Title (اسم دلخواهه)
  • نوع متغیر (Variable Type): DOM Element
  • روش انتخاب (Selection Method): CSS Selector
  • المان انتخابگر (Element Selector): h1 (یا هر کلاسی که عنوان مقاله‌هات داره، مثلاً .post-title h1)
  • Attribute Name: (اینو خالی بذار، چون ما خودِ «متن» تگ h1 رو می‌خوایم نه یه ویژگی خاصش رو)

۲. متغیر اسم نویسنده:

  • اسم متغیر: DOM - Author Name
  • نوع متغیر: DOM Element
  • روش انتخاب: CSS Selector
  • المان انتخابگر: مثلاً .author-box .name (اینجا باید بری تو سایتت و با Inspect Element ببینی اسم نویسنده دقیقاً تو چه کلاس CSSی قرار گرفته)

۳. متغیر تاریخ انتشار (یه کم سخته):

  • استخراج تاریخ با DOM سخته چون معمولاً فرمتش استاندارد اسکیما (ISO 8601) نیست.
  • راه ساده‌تر: اگه از متغیر داخلی {{Page Title}} (که معمولاً همون تایتل مرورگره) راضی هستی، می‌تونی برای عنوان از همون استفاده کنی.
  • نکته: برای تاریخ‌ها و عکس شاخص، اگه نتونی با DOM دربیاری، بهترین کار همون Data Layer هست. اما برای سادگی، فعلاً فرض می‌کنیم اینا رو نداریم یا دستی وارد می‌کنیم (که اصلاً داینامیک نیست!).

اعتراف دوستانه: همونطور که می‌بینی، این روش برای اطلاعات پیچیده‌ای مثل تاریخ آپدیت یا URL عکس شاخص، داره ضعیف عمل می‌کنه. GTM برای این ساخته شده که از Data Layer بخونه. اگه Data Layer نداری، روش PHP (روش اول) خیلی دقیق‌تر و قوی‌تره.

(کد نمونه) ساخت تگ “Custom HTML” و قرار دادن قالب اسکیما با متغیرهای GTM

حالا می‌ریم سراغ ساخت «تگ» (Tag) اصلی.

۱. برو به Tags > New.

۲. اسمش رو بذار: Schema – Article – Blog Posts.

۳. روی Tag Configuration کلیک کن و نوع تگ Custom HTML رو انتخاب کن.

۴. حالا اون قالب اسکیمای گام صفر رو یادت میاد؟ همون رو کپی می‌کنیم اینجا، ولی این بار با متغیرهای GTM:

HTML
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "{{DOM - Page Title}}", // متغیری که خودمون ساختیم
  
  // "datePublished": "...", // همونطور که گفتم، درآوردن اینا با DOM سخته
  // "dateModified": "...",
  
  "author": {
    "@type": "Person",
    "name": "{{DOM - Author Name}}" // متغیری که خودمون ساختیم
  },
  
  "publisher": {
    "@type": "Organization",
    "name": "وزیر سئو", // این ثابته
    "logo": {
      "@type": "ImageObject",
      "url": "https://vazirseo.com/logo.png" // اینم ثابته
    }
  },
  
  // "image": { ... }, // درآوردن URL عکس شاخص و ابعادش هم با DOM سخته
  
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "{{Page URL}}" // استفاده از متغیر داخلی GTM
  }
}
</script>

مثال: استفاده از {{Page Title}} یا {{Data Layer Variable – postTitle}}

  • همونطور که دیدی، ما برای عنوان از متغیری که خودمون ساختیم ({{DOM - Page Title}}) استفاده کردیم تا مطمئن شیم دقیقاً متن <h1> رو برمی‌داره.
  • می‌تونستیم از متغیر داخلی {{Page Title}} هم استفاده کنیم، ولی اون تایتل مرورگر (تگ <title>) رو برمی‌گردونه که شاید با عنوان مقاله یکی نباشه.
  • اگه Data Layer داشتیم، کارمون تمیز بود و مثلاً از {{dlv - postTitle}} (مخفف Data Layer Variable) استفاده می‌کردیم که خیلی دقیق بود.

تنظیم تریگر (Trigger) دقیق: اجرای تگ فقط در صفحات مقالات (مثال: Page Path contains /blog/)

این بخش «حیاتی» ماجراست. ما نمی‌خوایم این تگ توی همه‌ی صفحات سایت اجرا (Fire) بشه.

۱. زیر همون تگ، روی بخش Triggering کلیک کن.

۲. یه تریگر جدید بساز (با علامت +).

۳. اسمش رو بذار Page View – Blog Posts.

۴. نوع تریگر (Trigger Type) رو Page View انتخاب کن.

۵. بذارش روی Some Page Views.

۶. حالا شرط رو تنظیم کن:

{{Page Path}} | contains | /blog/

(تو باید اینجا آدرسی رو بذاری که بین همه‌ی مقاله‌های سایتت مشترکه. مثلاً اگه آدرس مقاله‌هات اینجوریه site.com/blog/my-post/، پس /blog/ شرط خوبیه. اگه نه، شاید لازم باشه از matches Regex (ریجکس) استفاده کنی که یه کم پیشرفته‌تره.)

۷. تریگر رو Save کن و تگ رو هم Save کن.

۸. یادت نره حتماً Submit (و Publish) کنی تا تغییراتت زنده بشن!

جمع‌بندی این روش

روش GTM فوق‌العاده قدرتمنده، به شرطی که تو دیتای مورد نیازت رو توی Data Layer داشته باشی. اگه بخوای با DOM Scraping کار کنی، روش شکننده‌ایه و احتمالاً نمی‌تونی تمام فیلدهای اسکیما (مثل تاریخ آپدیت و ابعاد عکس) رو به صورت داینامیک پُر کنی.

حالا تو بگو، فکر می‌کنی کدوم روش برای سایت تو مناسب‌تره؟ چالش PHP یا انعطاف‌پذیری GTM؟

خب، تا اینجا همه‌چی عالی به نظر می‌رسید، نه؟ ما کد داینامیک رو نوشتیم و حس می‌کنیم همه‌چی اتوماتیک شد و می‌تونیم بریم یه فنجون قهوه بخوریم.

اما… (اینجا دقیقاً همون‌جاییه که من خودم چند بار بدجوری خوردم به دیوار!)

پیاده‌سازی داینامیک یه سری «تله» خیلی ریز و فنی داره که اگه حواست بهشون نباشه، نه تنها اسکیما برات کار نمی‌کنه، بلکه کل ساختار JSON رو می‌شکنی و گوگل یه ارور قرمز گنده تو سرچ کنسول بهت نشون می‌ده.

بیا چند تا از این کابوس‌های واقعی و تجربه‌های عملی رو با هم مرور کنیم.

(تجربه عملی) اشتباهات رایج در پیاده‌سازی داینامیک اسکیما

اینا اشتباهاتی هستن که شاید تو هیچ کتابی ننویسن و فقط وقتی باهاشون مواجه می‌شی که کارت رو تست می‌کنی.

خطای مرگبار: کاراکترهای نقل قول (Quotes) و شکستن ساختار JSON

این کابوس شماره یک منه. ببین، کل ساختار JSON با دابل کوتیشن (") کار می‌کنه. مثلاً: "headline": "عنوان مقاله من". همه‌چی ساده‌ست.

حالا فرض کن نویسنده‌ی تو یه مقاله‌ای نوشته با این عنوان:

بررسی گوشی “iPhone 15” در سال جدید

وقتی کد داینامیک ما می‌خواد اینو بذاره تو قالب، نتیجه چی می‌شه؟ می‌شه این:

"headline": "بررسی گوشی "iPhone 15" در سال جدید"

دیدی چی شد؟ JSON تا وسط راه می‌خونه ("بررسی گوشی ") و به اون کوتیشن دوم (قبل از iPhone) که می‌رسه، فکر می‌کنه متن تموم شده! بقیه‌ی عنوان (iPhone 15" در سال جدید") براش دیگه معنی نداره و… بوم! 💥 کل ساختار شکست.

گوگل اصلاً نمی‌تونه این اسکیما رو بخونه. انگار داری وسط جمله، یه پرانتز باز کنی ولی هیچ‌وقت نبندیش.

مشکل رایج: خالی بودن متغیرها (Null Variables) و ایجاد خطای اسکیما (مثال: مقاله‌ای که تصویر شاخص ندارد)

این یکی هم خیلی رایجه. ما تو کد PHP (روش اول) خیلی شیک نوشتیم که برو URL عکس شاخص رو بردار (get_the_post_thumbnail_url). همه‌چی خوب کار می‌کنه تا اینکه…

یکی از نویسنده‌ها یادش می‌ره برای یه مقاله عکس شاخص بذاره!

اونوقت چی می‌شه؟ اون تابع PHP به جای آدرس عکس، یه مقدار null (خالی) یا false برمی‌گردونه. کد داینامیک ما هم همون مقدار «خالی» رو می‌ذاره تو بخش image اسکیما.

نتیجه؟ گوگل تو سرچ کنسول بهت ارور می‌ده که: «هی! تو فیلد image رو گذاشتی، ولی url توش خالیه!» یا «مقدار نامعتبره».

این اتفاق برای تاریخ آپدیت، اسم نویسنده (اگه مثلاً پست مهمان باشه و پروفایل نداشته باشه) یا هر متغیر دیگه‌ای که ممکنه خالی باشه، میفته. ما همیشه باید برای این «خالی» بودن‌ها یه فکری بکنیم و یه شرط بذاریم که اگه متغیر خالی بود، اصلاً اون بخش از اسکیما رو توی کد نذاره.

خطای منطقی: استفاده از اسکیمای Article برای صفحات دسته‌بندی

یادته تو روش PHP چقدر تاکید کردم که حتماً از شرط if ( is_single() ) استفاده کنیم؟ این دلیلشه.

یه بار تو پروژه‌ای دیدم که همکارم یادش رفته بود اون شرط رو بذاره. نتیجه فاجعه بود. اسکیمای Article (مقاله) ما داشت تو همه‌ی صفحات سایت لود می‌شد!

تصور کن صفحه «دسته‌بندی بلاگ» (Category Page) تو، به گوگل بگه «سلام! من یه مقاله هستم!» یا صفحه «درباره ما» بگه من یه BlogPosting ام! این یه خطای منطقی وحشتناکه.

گوگل عاشق «دقت» و «ارتباط» (Relevancy) هست. وقتی تو بهش اطلاعات اشتباه می‌دی، اعتمادش رو از دست می‌ده. ما باید مطمئن شیم اسکیمای Article فقط برای مقاله‌ها، اسکیمای Product فقط برای محصولات و اسکیمای CollectionPage (یا ItemList) فقط برای دسته‌بندی‌ها اجرا می‌شه. قاطی کردن اینا یعنی سردرگم کردن گوگل.

(راه حل) استفاده از توابع PHP برای “Escape” کردن صحیح رشته‌ها

خب، حالا راه حل اون کابوس اول (مشکل کوتیشن‌ها) چیه؟

خوشبختانه وردپرس (و PHP) برامون راه‌حل گذاشته. به این کار می‌گن «اِسکیپ کردن» (Escaping). یعنی ما به PHP می‌گیم قبل از اینکه اون عنوان مقاله رو بذاری تو JSON، یه نگاهی بهش بنداز. اگه توش کاراکتر دردسرسازی مثل " دیدی، یه لحظه خنثی‌ش کن.

مثلاً کاراکتر " رو تبدیل می‌کنه به \". اینجوری اون کوتیشن دیگه برای JSON معنی «پایان متن» رو نمی‌ده و فقط به عنوان یه کاراکتر معمولی خونده می‌شه.

توابع زیادی برای این کار هست، مثل esc_js() یا esc_attr().

اما راه حل طلایی چیه؟ اینه که به جای اینکه خودمون دونه دونه متغیرها رو escape کنیم، از همون اول کل آرایه‌ی PHP (که تو روش اول ساختیم) رو بدیم به تابع json_encode. این تابع خودش اونقدر باهوش هست که همه‌ی این کاراکترهای مشکل‌ساز رو برامون خنثی کنه و یه خروجی JSON تمیز و بی‌نقص بهمون تحویل بده. (دقیقاً همون کاری که ما تو کد نمونه‌ی روش اول با json_encode انجام دادیم!)

خب، تبریک می‌گم! 🥳

ما کار سخت رو انجام دادیم. چه با PHP بوده باشه چه با GTM، الان یه سیستم داینامیک داریم که داره (احتمالاً) کار می‌کنه. اما تو دنیای سئو، «احتمالاً» کلمه‌ی خطرناکیه. گام نهایی، یعنی تست کردن، به اندازه‌ی خودِ پیاده‌سازی مهمه.

اینجا مرحله‌ایه که می‌فهمیم اون دستور پختی که نوشتیم، تهش یه کیک خوشمزه شده یا یه تیکه زغال!

گام نهایی: اعتبارسنجی و تست اسکیمای داینامیک

باید مطمئن شیم همه‌چی همون‌طور که انتظار داشتیم داره کار می‌کنه و اون متغیرها (%%...%%) دارن به درستی با دیتای واقعی پُر می‌شن.

چگونه تست کنیم که متغیرها به درستی پر شده‌اند؟ (View Source در مقابل Inspect Element)

این اولین و سریع‌ترین تستیه که من خودم همیشه انجام می‌دم و یه تفاوت فنی مهم اینجا وجود داره:

۱. View Source (کلیک راست > View Page Source یا Ctrl+U):

  • اینجا شما کد خام HTML رو می‌بینید؛ یعنی دقیقاً همون چیزی که سرور شما به گوگل (و مرورگر) تحویل داده.
  • اگر از روش PHP (فایل functions.php) استفاده کردی، کد اسکیمای JSON-LD تو باید اینجا معلوم باشه. اگه نیست، یعنی هوک wp_head تو اصلاً اجرا نشده.

۲. Inspect Element (کلیک راست > Inspect):

  • اینجا شما کد زنده (Rendered) رو می‌بینید؛ یعنی کدی که توسط مرورگر (و جاوا اسکریپت) تغییر کرده.
  • اگر از روش Google Tag Manager (GTM) استفاده کردی، کد اسکیمای تو فقط و فقط اینجا دیده می‌شه (چون GTM با جاوا اسکریپت اون رو «تزریق» کرده).

تست چیه؟

تو هر کدوم از اینا که رفتی (بسته به روش پیاده‌سازیت)، دنبال تگ <script type=”application/ld+json”> بگرد. وقتی پیداش کردی، باید ببینی که آیا اون متغیرهای ما (مثل %%POST_TITLE%% یا {{DOM – Page Title}}) از بین رفتن و به جاشون دیتای واقعی (مثلاً “headline”: “آموزش کامل سئو کلاه سفید”) نشسته یا نه.

اگه هنوز خودِ اسم متغیر رو می‌بینی، یعنی یه جای کار می‌لنگه و سیستم نتونسته دیتای واقعی رو جایگزین کنه.

استفاده از ابزار رسمی Google Rich Results Test برای یک URL نمونه

بعد از اینکه چشمی چک کردی و دیدی کد وجود داره، وقتشه بدیم دست خودِ «گوگل» تستش کنه. این ابزار، رفیق صمیمی ماست و حرف آخر رو می‌زنه.

۱. برو به Google Rich Results Test.

۲. آدرس (URL) یکی از مقاله‌هایی که مطمئنی باید اسکیما داشته باشه رو وارد کن.

۳. دکمه «Test URL» رو بزن.

حالا منتظر نتیجه باش:

  • سبز (Valid): 🥳 تبریک! یعنی گوگل هم اسکیمای تو رو شناسایی کرده و هم ساختارش درسته. معمولاً بهت نشون می‌ده که کدوم نوع اسکیما رو پیدا کرده (مثلاً Article).
  • نارنجی (Valid with warnings): یعنی ساختار کلی درسته، ولی گوگل داره دوستانه بهت می‌گه «بهتر بود فلان فیلد (مثلاً dateModified یا image) رو هم می‌ذاشتی.» اینا خطا نیستن، ولی برای کامل بودن بهتره اضافه‌شون کنی.
  • قرمز (Invalid – Errors): ⛔️ این یعنی فاجعه! یعنی یه جای کارت اساسی می‌لنگه. معمولاً به خاطر همون اشتباهات رایجه (مثل مشکل کوتیشن‌ها یا خالی بودن یه فیلد ضروری). خود ابزار معمولاً بهت می‌گه کدوم خط از کد JSON تو شکسته.

من خودم حتی وقتی تیک سبز رو می‌گیرم، حتماً روی اسم اسکیمای شناسایی شده (مثلاً Article) کلیک می‌کنم تا ببینم آیا مقادیر رو هم درست خونده یا نه. (مثلاً آیا headline همون عنوان مقاله‌ست؟)

بررسی گزارش “Pages” در سرچ کنسول برای اطمینان از شناسایی اسکیما در سطح سایت

خب، تست «Rich Results» فقط برای یه دونه URL بود. ما باید مطمئن شیم این سیستم داینامیک داره برای همه‌ی مقاله‌های ما کار می‌کنه. اینجا پای سرچ کنسول (GSC) میاد وسط. این دیگه تست نیست، این «مانیتورینگ» دائمیه.

۱. وارد سرچ کنسول سایتت شو.

۲. تو منوی سمت چپ، بیا پایین تا به بخش «Enhancements» (بهبودها) برسی.

۳. زیر این بخش، باید دنبال گزارش مخصوص اسکیمایی که پیاده کردی بگردی (مثلاً Articles یا Blog postings یا Products).

تو به گزارش “Pages” اشاره کردی؛ اون گزارش بیشتر در مورد «ایندکس» شدن صفحاته. اما گزارش‌های بخش “Enhancements” دقیقاً برای مانیتور کردن «اسکیما» و «نتایج غنی» (Rich Results) ساخته شدن.

وقتی روی گزارش Articles کلیک می‌کنی، گوگل بهت یه نمودار نشون می‌ده:

  • Valid (سبز): تعداد صفحاتی که اسکیمای Article صحیح دارن. این عدد باید کم‌کم رشد کنه و به تعداد کل مقاله‌های سایتت برسه.
  • Valid with warnings (نارنجی): صفحاتی که مشکل کوچیک دارن (همون توصیه‌های گوگل).
  • Invalid (قرمز): صفحاتی که اسکیمای شکسته یا ناقص دارن.

وظیفه تو اینه که این گزارش رو هر چند وقت یه بار چک کنی (من خودم هفتگی چک می‌کنم). اگه یهو دیدی تعداد صفحات «Invalid» زیاد شد، یعنی احتمالاً یه تغییری تو قالب سایت دادی یا یه پستی منتشر کردی (مثلاً بدون عکس شاخص) که سیستم داینامیک تو نتونسته از پسش بربیاد و باید بری درستش کنی.

جمع‌بندی (نتیجه‌گیری تعاملی)

خب، اینم از سفرمون به دنیای «اتوماتیک‌سازی» اسکیما.

دیدیم که چطور پلاگین‌های عمومی و روش کپی/پیست کردن دستی، مثل یه چسب‌زخم موقتی‌ان و چطور «تزریق داینامیک» (چه با جادوی functions.php و چه با انعطاف‌پذیری GTM) می‌تونه یه بار برای همیشه ما رو از کابوس آپدیت دستی و خطاهای انسانی نجات بده.

این روش فقط برای قشنگی یا گرفتن ستاره‌های طلایی نیست؛ این کار برای ساختن «دقت» و «اعتماد» نزد گوگل حیاتیه. ما با این کار داریم به گوگل نشون می‌دیم که اطلاعات سایتمون همیشه به‌روز، قابل اتکا و دقیقاً همونیه که کاربر می‌بینه.

آره، قبول دارم راه‌اندازیش شاید یه کم کار فنی داشته باشه، اما اون آرامش خیالی که بعدش بهت می‌ده و وقتی که برات ذخیره می‌کنه، شک نکن که ارزشش رو داره. تو این مسیر، حواسمون به تله‌های مرگباری مثل کوتیشن‌ها و متغیرهای خالی هم بود و یاد گرفتیم چطور با ابزارهای خود گوگل، مثل یه کارآگاه حرفه‌ای، کارمون رو تست کنیم.

حالا تو بهم بگو، با تجربه‌ای که از سایت خودت داری، کدوم روش رو انتخاب می‌کنی؟ چالش فنی و کنترل مطلق PHP یا انعطاف‌پذیری و مدیریت راحت‌تر GTM؟ خیلی دوست دارم نظرت رو بدونم.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *