Block Context در وردپرس یک مکانیزم رسمی برای اشتراک‌گذاری داده میان بلاک والد و بلاک‌های فرزند است که بدون آن، هر بلاک مجبور می‌شود داده‌های مشترک را به‌صورت مستقل مدیریت کند و این امر به تکرار کد و ناهماهنگی منجر می‌شود.

Block Context در وردپرس یک قرارداد رسمی برای انتقال داده از بلاک والد به بلاک‌های فرزند است که از طریق کلیدهای providesContext و usesContext در فایل block.json تعریف می‌شود.

این مکانیزم جایگزین پاس دادن مستقیم Props در ساختار بلاک‌های تودرتو می‌شود و از prop drilling جلوگیری می‌کند.

Context در ویرایشگر از طریق Data API و در فرانت‌اند از طریق یک ساختار سراسری مدیریت می‌شود و در هر دو محیط قابل دسترسی است.

بدون Block Context، ساخت بلاک‌هایی مانند Columns، Query Loop و Navigation که نیاز به هماهنگی میان فرزندان دارند، عملاً غیرممکن می‌شود.

در این مقاله معماری، الگوهای تعریف، دسترسی در PHP و JavaScript، اشتباهات رایج و نکات مهندسی Block Context را با مثال‌های واقعی بررسی می‌کنیم.

نخستین بار که یک بلاک فرزند برای Query Loop هسته وردپرس ساختم، انتظار داشتم که پست جاری به‌طور خودکار در اختیار بلاک قرار بگیرد. وقتی دیدم چنین چیزی اتفاق نمی‌افتد، متوجه شدم که باید از Block Context استفاده کنم تا بلاک فرزند بتواند از داده‌های بلاک والد بهره‌مند شود. آن تجربه نقطه شروعی شد برای بررسی دقیق‌تر این مکانیزم و نقش آن در معماری بلاک‌های تودرتو. آنچه در ادامه می‌خوانید حاصل کار عملی با Context در پروژه‌های واقعی و بررسی کد منبع گوتنبرگ است.

Block Context چیست و چه نیازی را برطرف می‌کند؟

Block Context یک مکانیزم رسمی در گوتنبرگ است که به بلاک والد اجازه می‌دهد بخشی از داده‌های خود را در اختیار بلاک‌های فرزند قرار دهد، بدون آنکه نیاز به پاس دادن مستقیم Props باشد. این مکانیزم از دو کلید اصلی در فایل block.json استفاده می‌کند: providesContext که در بلاک والد تعریف می‌شود و usesContext که در بلاک فرزند تعریف می‌گردد. برای آشنایی با مبانی ساخت بلاک، مقاله آموزش ساخت بلوک سفارشی گوتنبرگ را مطالعه کنید.

نیازی که Block Context برطرف می‌کند، ماهیت ساختار درختی بلاک‌های گوتنبرگ است. در یک ساختار تودرتو، بلاک فرزند ممکن است به داده‌هایی نیاز داشته باشد که تنها در بلاک والد موجود است. برای مثال، در بلاک Query Loop، هر بلاک فرزند باید بداند که کدام پست جاری در حال نمایش است. بدون یک مکانیزم اشتراک‌گذاری، هر بلاک فرزند مجبور می‌شود این داده را به‌صورت مستقل کشف کند یا آن را از طریق زنجیره‌ای از Props دریافت نماید. Context این فرآیند را به یک قرارداد ساده و مستقیم تبدیل می‌کند. برای مطالعه بیشتر درباره این ساختار، مقاله چرا InnerBlocks در وردپرس کلید ساخت بلاک‌های حرفه‌ای است؟ را ببینید.

Context یک کانال ارتباطی یک‌طرفه است که از والد به فرزند جریان می‌یابد و از پاس دادن مستقیم Props در چند سطح جلوگیری می‌کند.

نکته مهمی که در بررسی‌های خود به آن پی بردم این است که Block Context در واقع یک پیاده‌سازی از مفهوم Dependency Injection (تزریق وابستگی) در سطح بلاک است. به‌جای آنکه بلاک فرزند به‌طور صریح به والد خود وابسته باشد، تنها به یک قرارداد مشخص (نام Context) وابسته است و والد می‌تواند هر مقداری را در آن قرار دهد. این رویکرد، جداسازی نگرانی‌ها را تقویت می‌کند و امکان استفاده مجدد از بلاک فرزند در چندین والد مختلف را فراهم می‌آورد. برای مطالعه بیشتر درباره مفهوم Context در علوم کامپیوتر، می‌توانید صفحه Context (computing) را در ویکی‌پدیا ببینید.

Block Context در ویرایشگر از طریق Data API مدیریت می‌شود و در فرانت‌اند از طریق یک ساختار سراسری که در زمان رندر بلاک‌ها تولید می‌گردد. این دو مکانیزم با یکدیگر هماهنگ هستند و تضمین می‌کنند که رفتار بلاک در ویرایشگر و فرانت‌اند یکسان باشد. برای مطالعه بیشتر درباره مدیریت state در ویرایشگر، مقاله Data API در وردپرس چیست و چطور state بلاک‌ها را مدیریت می‌کند؟ را ببینید.

چرا اشتراک داده بین بلاک‌ها حیاتی است؟

اشتراک داده میان بلاک‌ها در ساختار تودرتو نه یک انتخاب لوکس، بلکه یک ضرورت معماری است. در ادامه به بررسی دلایل این اهمیت می‌پردازیم.

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

دومین دلیل، جلوگیری از prop drilling است. اگر Context وجود نداشت، برای انتقال داده از یک بلاک والد به یک بلاک نوه (grandchild)، باید داده از طریق بلاک فرزند میانی پاس داده می‌شد. این رویکرد، prop drilling نامیده می‌شود و به کدی پیچیده و شکننده منجر می‌گردد. Context این زنجیره را حذف می‌کند و امکان دسترسی مستقیم بلاک نوه به داده بلاک والد را فراهم می‌آورد.

سومین دلیل، پشتیبانی از استفاده مجدد است. یک بلاک فرزند که از Context استفاده می‌کند، می‌تواند در چندین بلاک والد مختلف بدون تغییر کد قرار گیرد. برای مثال، یک بلاک نمایش عنوان پست می‌تواند درون Query Loop، Latest Posts و هر بلاک دیگری که Context پست جاری را فراهم می‌کند، کار کند. این انعطاف‌پذیری، یکی از مزایای اصلی Context محسوب می‌شود. برای مطالعه بیشتر درباره ساختار بلاک‌ها، مقاله چرا باید بلوک سفارشی گوتنبرگ بسازیم وقتی افزونه‌های آماده وجود دارند؟ را ببینید.

چهارمین دلیل، هماهنگی خودکار در بلاک‌های هسته وردپرس است. بلاک‌هایی مانند Query Loop، Columns و Navigation از Context برای هماهنگی میان بلاک‌های فرزند خود استفاده می‌کنند. بدون این مکانیزم، رفتار این بلاک‌ها نمی‌توانست به این سطح از انسجام برسد.

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

معماری Block Context در گوتنبرگ

برای درک عمیق Block Context، باید معماری آن را در چند لایه بررسی کنیم. این معماری بر پایه چند مفهوم بنیادین بنا شده است که درک هر یک برای کار حرفه‌ای ضروری است.

لایه تعریف در block.json

اولین لایه، تعریف Context در فایل block.json است. بلاک والد از طریق کلید providesContext اعلام می‌کند که کدام داده را در اختیار فرزندان قرار می‌دهد و بلاک فرزند از طریق کلید usesContext اعلام می‌کند که به کدام Context نیاز دارد.

لایه توزیع در ویرایشگر

دومین لایه، توزیع Context در ویرایشگر است. ویرایشگر از طریق Data API و مکانیزم Block Context Provider، مقادیر Context را در اختیار بلاک‌های فرزند قرار می‌دهد. این فرآیند به‌صورت خودکار در زمان رندر بلاک‌ها انجام می‌شود.

لایه توزیع در فرانت‌اند

سومین لایه، توزیع Context در فرانت‌اند است. در زمان رندر بلاک‌ها در فرانت‌اند، وردپرس یک ساختار سراسری از Contextها تولید می‌کند و آن را در اختیار بلاک‌های فرزند قرار می‌دهد. این ساختار از طریق پارامتر $block در تابع render_callback قابل دسترسی است.

لایه دسترسی در JavaScript

چهارمین لایه، دسترسی به Context در JavaScript است. در ویرایشگر، بلاک فرزند می‌تواند از طریق پارامتر context در تابع edit به Context دسترسی پیدا کند. این پارامتر به‌طور خودکار توسط ویرایشگر پر می‌شود.

لایه مکانیزم محیط
تعریف providesContext / usesContext block.json
توزیع Block Context Provider ویرایشگر
دسترسی JS پارامتر context تابع edit
دسترسی PHP پارامتر $block->context render_callback

providesContext: سمت ارائه‌دهنده

کلید providesContext در بلاک والد تعریف می‌شود و مشخص می‌کند که کدام Attributes بلاک باید در اختیار بلاک‌های فرزند قرار گیرند. ساختار آن یک شیء است که هر کلید آن، نام Context و هر مقدار، نام Attribute مربوطه در بلاک والد است.

{
  "name": "my-plugin/container",
  "attributes": {
    "theme": {
      "type": "string",
      "default": "light"
    },
    "columns": {
      "type": "number",
      "default": 2
    }
  },
  "providesContext": {
    "my-plugin/theme": "theme",
    "my-plugin/columns": "columns"
  }
}

در این کد، بلاک والد دو Context را ارائه می‌دهد: my-plugin/theme که به Attribute theme نگاشت می‌شود و my-plugin/columns که به Attribute columns نگاشت می‌گردد. نام Context معمولاً با یک namespace مشخص می‌شود تا از تداخل با Contextهای سایر بلاک‌ها جلوگیری گردد. برای مطالعه بیشتر درباره ساختار Attribute، مقاله Block Attributes در وردپرس چیست و مدیریت داده‌های بلاک چطور انجام می‌شود؟ را ببینید.

providesContext یک قرارداد عمومی است؛ هر بلاک فرزندی که نام این Context را بداند، می‌تواند از آن استفاده کند.

ارائه Context از منبع غیر Attribute

در برخی موارد، ممکن است بخواهید Context را از منبعی غیر از Attributes بلاک والد ارائه دهید. برای مثال، ممکن است مقدار Context بر اساس تنظیمات سراسری سایت یا context جاری محاسبه شود. در این حالت، می‌توانید از فیلتر block_type_metadata_settings برای تغییر مقدار Context استفاده کنید یا در تابع edit بلاک والد، مقدار Context را به‌صورت پویا محاسبه نمایید.

usesContext: سمت مصرف‌کننده

کلید usesContext در بلاک فرزند تعریف می‌شود و مشخص می‌کند که بلاک به کدام Contextها نیاز دارد. این کلید یک آرایه از نام Contextها است.

{
  "name": "my-plugin/card",
  "usesContext": [
    "my-plugin/theme",
    "my-plugin/columns"
  ]
}

زمانی که یک بلاک فرزند از usesContext استفاده می‌کند، ویرایشگر به‌طور خودکار مقادیر Context را از بلاک والد استخراج کرده و در پارامتر context تابع edit قرار می‌دهد. در فرانت‌اند نیز همان مقادیر از طریق پارامتر $block->context در دسترس هستند.

دسترسی به Context در تابع edit

const Edit = ( { attributes, context } ) => {
  const theme = context[ 'my-plugin/theme' ] || 'light';
  const columns = context[ 'my-plugin/columns' ] || 2;

  return (
    <div className={ `my-card theme-${ theme }` }>
      <p>تعداد ستون‌ها: { columns }</p>
    </div>
  );
};

در این کد، بلاک فرزند مقادیر Context را از پارامتر context دریافت کرده و بر اساس آن‌ها، ظاهر خود را تنظیم می‌کند. توجه کنید که همیشه باید مقدار پیش‌فرض مناسبی برای Context در نظر بگیرید تا در صورت عدم دسترسی، بلاک به‌درستی کار کند. برای مطالعه بیشتر درباره کامپوننت‌های React، مقاله چرا بدون React نمی‌توان بلاک حرفه‌ای در وردپرس ساخت؟ را ببینید.

دسترسی به Context در تابع save

در بلاک‌های Static، تابع save نیز می‌تواند به Context دسترسی داشته باشد. اما توجه داشته باشید که در تابع save، مقادیر Context به‌صورت زنده در زمان نمایش در دسترس نیستند و تنها در زمان ذخیره ثبت می‌شوند. اگر مقدار Context در طول زمان تغییر کند، بلاک ممکن است نامعتبر شود. برای مطالعه بیشتر درباره این موضوع، مقاله Block Deprecation در وردپرس چیست و چرا نسخه‌های قدیمی بلاک را می‌شکند؟ را ببینید.

default و مقدار پیش‌فرض Context

در نسخه‌های اخیر گوتنبرگ، امکان تعریف مقدار پیش‌فرض برای Context اضافه شده است. این ویژگی به بلاک فرزند اجازه می‌دهد که در صورت عدم وجود بلاک والد، مقدار پیش‌فرض مناسبی دریافت کند.

{
  "usesContext": [ "my-plugin/theme" ],
  "context": {
    "my-plugin/theme": "light"
  }
}

در این ساختار، اگر بلاک فرزند درون بلاکی قرار گیرد که Context مربوطه را ارائه نمی‌دهد، مقدار light به‌عنوان پیش‌فرض استفاده می‌شود. این مکانیزم از بروز خطاهای undefined جلوگیری می‌کند و تجربه کاربری را بهبود می‌بخشد.

مقدار پیش‌فرض در سطح attribute

در بلاک والد، می‌توانید مقدار پیش‌فرض Context را از طریق مقدار پیش‌فرض Attribute تعریف کنید. زمانی که بلاک والد رندر می‌شود و Attribute مربوطه مقدار پیش‌فرض خود را دارد، همان مقدار در اختیار بلاک فرزند قرار می‌گیرد.

{
  "attributes": {
    "theme": {
      "type": "string",
      "default": "light"
    }
  },
  "providesContext": {
    "my-plugin/theme": "theme"
  }
}

این رویکرد، ساده‌ترین راه برای تعریف مقدار پیش‌فرض Context است و از تعریف جداگانه در بلاک فرزند جلوگیری می‌کند. برای مطالعه بیشتر درباره Attributeهای پیشرفته، مقاله Block Supports در وردپرس چیست و چطور امکانات بلاک را کنترل کنیم؟ را ببینید.

دسترسی به Context در ویرایشگر

در ویرایشگر، دسترسی به Context از طریق پارامتر context که به تابع edit پاس داده می‌شود، انجام می‌گیرد. این پارامتر یک شیء است که کلیدهای آن، نام Contextها و مقادیر آن، مقادیر فعلی Context می‌باشند.

الگوی Consumption در ویرایشگر

const Edit = ( { context, setAttributes } ) => {
  const theme = context[ 'my-plugin/theme' ];
  const columns = context[ 'my-plugin/columns' ];

  useEffect( () => {
    if ( theme ) {
      setAttributes( { localTheme: theme } );
    }
  }, [ theme ] );

  return (
    <div className={ `theme-${ theme }` }>
      <p>ستون‌ها: { columns }</p>
    </div>
  );
};

در این کد، بلاک فرزند مقادیر Context را می‌خواند و در صورت نیاز، آن‌ها را به‌عنوان Attribute محلی ذخیره می‌کند. این رویکرد به بلاک اجازه می‌دهد که از Context والد بهره‌مند شود اما همچنان امکان سفارشی‌سازی مستقل را داشته باشد.

استفاده از useSelect برای دسترسی به Context

در برخی موارد، ممکن است بخواهید Context را از طریق Data API و به‌صورت reactive بخوانید. برای این کار، می‌توانید از هوک useSelect استفاده کنید. برای مطالعه بیشتر درباره این هوک، مقاله useSelect و useDispatch در توسعه بلاک وردپرس چطور کار می‌کنند؟ را ببینید.

import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';

const Edit = ( { clientId } ) => {
  const theme = useSelect( ( select ) => {
    return select( blockEditorStore ).getBlockAttributes( clientId ).theme;
  }, [ clientId ] );

  return <div className={ `theme-${ theme }` }>...</div>;
};

این رویکرد به‌ویژه در بلاک‌هایی که نیاز به واکنش بلادرنگ به تغییرات Context دارند، مفید است.

مشکل رندر مجدد در ویرایشگر

یکی از نکات مهم در دسترسی به Context در ویرایشگر، مدیریت رندرهای مجدد است. اگر بلاک فرزند به‌طور مکرر Context را بخواند و بر اساس آن Attribute خود را تغییر دهد، ممکن است به حلقه بی‌پایان رندر منجر شود. برای جلوگیری از این مشکل، همیشه از آرایه وابستگی مناسب در useEffect استفاده کنید و تغییرات Attribute را تنها در صورت لزوم اعمال نمایید.

دسترسی به Context در فرانت‌اند

در فرانت‌اند، دسترسی به Context از طریق مکانیزم رندر بلاک‌ها انجام می‌گیرد. زمانی که وردپرس یک بلاک والد را رندر می‌کند، Contextهای ارائه‌شده را در یک ساختار سراسری ذخیره کرده و در زمان رندر بلاک‌های فرزند، آن‌ها را در اختیار قرار می‌دهد.

ساختار Block Context در فرانت‌اند

در فرانت‌اند، Context از طریق پارامتر $block در تابع render_callback قابل دسترسی است. این پارامتر یک شیء از کلاس WP_Block است که دارای پراپرتی context می‌باشد.

function my_plugin_render_child_block( $attributes, $content, $block ) {
  $theme = isset( $block->context[ 'my-plugin/theme' ] )
    ? $block->context[ 'my-plugin/theme' ]
    : 'light';

  $wrapper_attributes = get_block_wrapper_attributes( array(
    'class' => 'my-card theme-' . esc_attr( $theme ),
  ) );

  return '<div ' . $wrapper_attributes . '>' . $content . '</div>';
}

در این کد، بلاک فرزند مقدار Context را از $block->context استخراج کرده و آن را در کلاس CSS اعمال می‌کند. توجه کنید که همیشه باید مقدار پیش‌فرض مناسبی در نظر بگیرید تا در صورت عدم دسترسی، بلاک به‌درستی کار کند.

در فرانت‌اند، Context یک کانال یک‌طرفه از والد به فرزند است که در زمان رندر بلاک‌ها پر می‌شود.

الگوی اشتراک‌گذاری در PHP

در برخی موارد، ممکن است بخواهید Context را از PHP و به‌صورت پویا محاسبه کنید. برای این کار، می‌توانید از فیلتر render_block_context استفاده کنید:

function my_plugin_modify_block_context( $context, $parsed_block, $parent_block ) {
  if ( 'my-plugin/container' === $parsed_block[ 'blockName' ] ) {
    $context[ 'my-plugin/theme' ] = get_theme_mod( 'default_theme', 'light' );
  }
  return $context;
}
add_filter( 'render_block_context', 'my_plugin_modify_block_context', 10, 3 );

این فیلتر به شما امکان می‌دهد Context را بر اساس تنظیمات سایت یا شرایط دیگر تغییر دهید و از تعریف ثابت در block.json فراتر بروید.

دسترسی به Context در PHP و render_callback

در Dynamic Blocks، تابع render_callback مسئول تولید خروجی است و از این تابع می‌توان به Context دسترسی پیدا کرد. پارامتر سوم این تابع، یک شیء از کلاس WP_Block است که شامل پراپرتی context می‌باشد. برای مطالعه بیشتر درباره این نوع بلاک‌ها، مقاله Dynamic Blocks در وردپرس چیست و چرا رندر سرور مهم است؟ را ببینید.

ساختار کامل render_callback

function my_plugin_render_block( $attributes, $content, $block ) {
  $theme = isset( $block->context[ 'my-plugin/theme' ] )
    ? $block->context[ 'my-plugin/theme' ]
    : 'light';

  $query_args = array(
    'post_type'      => 'post',
    'posts_per_page' => absint( $attributes[ 'count' ] ),
  );

  if ( ! empty( $block->context[ 'my-plugin/categoryId' ] ) ) {
    $query_args[ 'cat'] = absint( $block->context[ 'my-plugin/categoryId' ] );
  }

  $query = new WP_Query( $query_args );
  // ... تولید خروجی
  wp_reset_postdata();
  return $output;
}

در این کد، بلاک فرزند از Context برای دریافت دسته‌بندی جاری استفاده می‌کند و بر اساس آن، پست‌های مرتبط را نمایش می‌دهد. این الگو در بلاک‌هایی مانند Query Loop و Related Posts کاربرد گسترده‌ای دارد.

دسترسی به Context در توابع کمکی

اگر نیاز به دسترسی به Context در توابع کمکی (Helper Functions) دارید، می‌توانید از تابع get_block_context استفاده کنید که در نسخه‌های اخیر وردپرس اضافه شده است. این تابع Context جاری را از یک بلاک استخراج می‌کند و نیازی به پاس دادن دستی ندارد.

$theme = get_block_context( 'my-plugin/theme' );
$category_id = get_block_context( 'my-plugin/categoryId', 0 );

این رویکرد، کد را ساده‌تر می‌کند و از تکرار منطق دسترسی به Context جلوگیری می‌نماید.

مثال عملی: ساخت بلاک والد و فرزند با Context

برای درک عملی Block Context، بیایید یک بلاک والد و یک بلاک فرزند بسازیم که از Context برای اشتراک داده استفاده می‌کنند. بلاک والد یک بلاک «Product Group» است که تنظیمات مشترک مانند رنگ و اندازه را نگهداری می‌کند و بلاک فرزند یک بلاک «Product Card» است که از این تنظیمات استفاده می‌نماید.

تعریف بلاک والد

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "my-plugin/product-group",
  "title": "Product Group",
  "category": "widgets",
  "attributes": {
    "themeColor": {
      "type": "string",
      "default": "blue"
    },
    "size": {
      "type": "string",
      "default": "medium"
    }
  },
  "providesContext": {
    "my-plugin/themeColor": "themeColor",
    "my-plugin/size": "size"
  },
  "editorScript": "file:./index.js",
  "style": "file:./style.css"
}

تعریف بلاک فرزند

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "my-plugin/product-card",
  "title": "Product Card",
  "category": "widgets",
  "usesContext": [
    "my-plugin/themeColor",
    "my-plugin/size"
  ],
  "attributes": {
    "title": { "type": "string", "default": "" }
  },
  "editorScript": "file:./index.js",
  "style": "file:./style.css"
}

پیاده‌سازی تابع edit بلاک فرزند

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, RichText } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
import metadata from './block.json';

registerBlockType( metadata.name, {
  edit: ( { attributes, setAttributes, context } ) => {
    const themeColor = context[ 'my-plugin/themeColor' ] || 'blue';
    const size = context[ 'my-plugin/size' ] || 'medium';

    const blockProps = useBlockProps( {
      className: `product-card theme-${ themeColor } size-${ size }`,
    } );

    return (
      <div { ...blockProps }>
        <RichText
          tagName="h3"
          value={ attributes.title }
          onChange={ ( title ) => setAttributes( { title } ) }
          placeholder={ __( 'نام محصول...', 'my-plugin' ) }
        />
      </div>
    );
  },
  save: ( { attributes } ) => {
    const blockProps = useBlockProps.save();
    return (
      <div { ...blockProps }>
        <h3>{ attributes.title }</h3>
      </div>
    );
  },
} );

در این کد، بلاک فرزند مقادیر themeColor و size را از Context می‌خواند و آن‌ها را در کلاس CSS اعمال می‌کند. اگر بلاک فرزند خارج از بلاک والد قرار گیرد، از مقادیر پیش‌فرض استفاده می‌شود.

Context در ساختارهای چندسطحی

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

عبور Context از بلاک‌های میانی

زمانی که یک بلاک والد، Context را ارائه می‌دهد، این Context در اختیار تمام بلاک‌های فرزند در هر سطحی قرار می‌گیرد. بلاک‌های میانی نیازی به تعریف usesContext ندارند و Context به‌طور خودکار از آن‌ها عبور می‌کند. این رویکرد، سادگی و انعطاف‌پذیری بالایی را فراهم می‌آورد.

Override کردن Context در سطح میانی

در برخی موارد، ممکن است بخواهید یک بلاک میانی مقدار Context را برای فرزندان خود تغییر دهد. برای این کار، کافی است که همان Context را در بلاک میانی نیز با providesContext تعریف کنید. در این حالت، مقدار جدید جایگزین مقدار والد می‌شود و بلاک‌های فرزند عمیق‌تر، مقدار جدید را دریافت می‌کنند.

{
  "name": "my-plugin/middle-block",
  "usesContext": [ "my-plugin/themeColor" ],
  "providesContext": {
    "my-plugin/themeColor": "localThemeColor"
  },
  "attributes": {
    "localThemeColor": {
      "type": "string",
      "default": "green"
    }
  }
}

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

Contextهای هسته وردپرس

وردپرس مجموعه‌ای از Contextهای استاندارد را در بلاک‌های هسته خود تعریف کرده است. آشنایی با این Contextها به شما امکان می‌دهد که بلاک‌های فرزند خود را با بلاک‌های هسته هماهنگ کنید.

Context در Query Loop

بلاک Query Loop مجموعه‌ای از Contextها را ارائه می‌دهد که در بلاک‌های فرزند قابل استفاده هستند:

  • postId: شناسه پست جاری
  • postType: نوع پست جاری
  • queryId: شناسه پرس‌وجوی جاری
  • query: پارامترهای پرس‌وجو

این Contextها به بلاک‌های فرزند اجازه می‌دهند که به اطلاعات پست جاری دسترسی داشته باشند و رفتار خود را بر اساس آن تنظیم کنند.

Context در Column و Columns

بلاک Columns مجموعه‌ای از Contextها را برای بلاک‌های Column ارائه می‌دهد:

  • isStackedOnMobile: وضعیت چیدمان در موبایل
  • verticalAlignment: تراز عمودی

این Contextها به بلاک Column اجازه می‌دهد که رفتار خود را با تنظیمات بلاک والد هماهنگ کند.

Context در Navigation

بلاک Navigation مجموعه‌ای از Contextها را برای بلاک‌های فرزند ارائه می‌دهد که شامل تنظیمات ظاهری و ساختاری منو می‌شود. این Contextها در بلاک‌های Navigation Link، Submenu و سایر بلاک‌های مرتبط کاربرد دارند.

بلاک والد Context ارائه‌شده کاربرد
Query Loop postId، postType نمایش پست جاری
Columns isStackedOnMobile چیدمان ریسپانسیو
Navigation تنظیمات ظاهری منو هماهنگی ظاهر منو

تفاوت Context با Attributes و Props

یکی از مباحثی که اغلب توسعه‌دهندگان تازه‌کار را سردرگم می‌کند، تفاوت میان Context، Attributes و Props است. در حالی که این سه مفهوم به‌هم مرتبط هستند، اما نقش‌های متفاوتی ایفا می‌کنند.

Attributes

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

Props

Props ورودی‌هایی هستند که یک کامپوننت React از والد خود دریافت می‌کند. در گوتنبرگ، Props شامل Attributes، setAttributes، isSelected و سایر مقادیر کنترلی هستند. Props تنها در محدوده کامپوننت معتبر هستند و در پایگاه داده ذخیره نمی‌شوند.

Context

Context یک کانال ارتباطی است که داده را از بلاک والد به بلاک‌های فرزند در هر سطحی منتقل می‌کند، بدون آنکه نیاز به پاس دادن مستقیم Props باشد. Context در پایگاه داده ذخیره نمی‌شود و تنها در زمان رندر بلاک‌ها در دسترس است.

ویژگی Attributes Props Context
ذخیره در پایگاه داده بله خیر خیر
جهت جریان محلی بلاک والد به فرزند مستقیم والد به تمام فرزندان
ماندگاری دائمی در طول رندر در طول رندر
مدیریت setAttributes والد providesContext

درک این تفاوت‌ها به شما امکان می‌دهد که داده‌ها را در جای مناسب نگهداری کنید و از بروز مشکلات عملکردی یا از دست رفتن داده جلوگیری نمایید.

Context در Dynamic Blocks

در Dynamic Blocks، Context نقش پررنگ‌تری ایفا می‌کند زیرا خروجی در زمان نمایش تولید می‌شود و Context می‌تواند بر تولید خروجی اثر بگذارد. برای مطالعه بیشتر درباره این نوع بلاک‌ها، مقاله Dynamic Blocks در وردپرس چیست و چرا رندر سرور مهم است؟ را ببینید.

استفاده از Context در render_callback

در render_callback، Context از طریق پارامتر سوم قابل دسترسی است و می‌تواند بر منطق تولید خروجی اثر بگذارد. برای مثال، یک بلاک Dynamic می‌تواند بر اساس Context دسته‌بندی، پست‌های مرتبط را نمایش دهد:

function my_plugin_render_related_posts( $attributes, $content, $block ) {
  $category_id = isset( $block->context[ 'my-plugin/categoryId' ] )
    ? absint( $block->context[ 'my-plugin/categoryId' ] )
    : 0;

  $query = new WP_Query( array(
    'post_type'      => 'post',
    'posts_per_page' => 3,
    'cat'            => $category_id,
  ) );

  // ... تولید خروجی
  wp_reset_postdata();
  return $output;
}

الگوی Context در Query Loop سفارشی

در بلاک‌هایی که مشابه Query Loop عمل می‌کنند، Context نقش کلیدی در هماهنگی فرزندان دارد. بلاک والد مسئول ارائه Context پست جاری است و بلاک‌های فرزند از این Context برای نمایش داده‌های پست استفاده می‌کنند. این الگو در ساخت بلاک‌های سفارشی نمایش محتوا کاربرد گسترده‌ای دارد.

عملکرد و هزینه‌های Context

Block Context از نظر عملکرد هزینه‌هایی دارد که باید در پروژه‌های بزرگ در نظر گرفته شود. در ادامه به بررسی این هزینه‌ها و راه‌های بهینه‌سازی می‌پردازیم.

تأثیر بر رندر ویرایشگر

هر بار که یک بلاک والد رندر می‌شود، Contextهای آن در یک ساختار سراسری ذخیره می‌شوند. اگر تعداد بلاک‌های تودرتو زیاد باشد، این ساختار می‌تواند حجم قابل‌توجهی داشته باشد و بر سرعت ویرایشگر تأثیر بگذارد. برای بهینه‌سازی، تنها Contextهای ضروری را ارائه دهید و از ارائه داده‌های حجیم خودداری کنید.

تأثیر بر رندر فرانت‌اند

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

کش کردن Context

در بلاک‌های Dynamic که از Context استفاده می‌کنند، می‌توانید خروجی را بر اساس Context کش کنید. برای این کار، می‌توانید از Transients یا Object Cache استفاده نمایید و مقادیر Context را در کلید کش لحاظ کنید. برای مطالعه بیشتر درباره کش، مقاله Block Serialization در وردپرس چیست و ذخیره بلاک‌ها چطور کار می‌کند؟ را ببینید.

کاهش تعداد Contextها

یکی از راه‌های بهینه‌سازی، کاهش تعداد Contextهای ارائه‌شده است. به‌جای ارائه هر Attribute به‌صورت جداگانه، می‌توانید یک Context ترکیبی ارائه دهید که شامل چند مقدار باشد. این رویکرد، حجم ساختار Context را کاهش می‌دهد و عملکرد را بهبود می‌بخشد.

اشتباهات رایج در کار با Context

در کار با Block Context، توسعه‌دهندگان اغلب مرتکب اشتباهاتی می‌شوند که منجر به کد شکننده یا تجربه کاربری ضعیف می‌شود. در ادامه به برخی از مهم‌ترین این اشتباهات اشاره می‌کنیم.

نادیده گرفتن مقدار پیش‌فرض Context

رایج‌ترین اشتباه، عدم تعریف مقدار پیش‌فرض برای Context در بلاک فرزند است. اگر بلاک فرزند خارج از بلاک والد قرار گیرد و مقدار پیش‌فرض تعریف نشده باشد، مقدار Context ممکن است undefined باشد و این می‌تواند به خطا منجر شود. همیشه مقدار پیش‌فرض مناسب تعریف کنید.

ارائه داده‌های حجیم در Context

برخی توسعه‌دهندگان تمام داده‌های بلاک والد را در Context قرار می‌دهند. این کار حجم ساختار Context را افزایش می‌دهد و بر عملکرد تأثیر می‌گذارد. تنها داده‌هایی را ارائه دهید که بلاک‌های فرزند واقعاً به آن نیاز دارند.

نادیده گرفتن namespace در نام Context

استفاده از نام‌های عمومی مانند theme یا color برای Context می‌تواند به تداخل با Contextهای سایر بلاک‌ها منجر شود. همیشه از یک namespace مشخص مانند my-plugin/theme استفاده کنید.

عدم هماهنگی با Contextهای هسته

اگر بلاک شما درون بلاک‌های هسته وردپرس مانند Query Loop یا Columns قرار می‌گیرد، باید با Contextهای هسته هماهنگ باشد. نادیده گرفتن این Contextها می‌تواند به ناسازگاری منجر شود.

نادیده گرفتن سازگاری ویرایشگر و فرانت‌اند

Context در ویرایشگر و فرانت‌اند به‌طور یکسان مدیریت می‌شود، اما گاهی ممکن است تفاوت‌هایی وجود داشته باشد. همیشه رفتار بلاک خود را در هر دو محیط تست کنید.

استفاده از Context برای داده‌های دوطرفه

Context یک کانال یک‌طرفه است و برای اشتراک داده از والد به فرزند طراحی شده است. اگر نیاز به ارسال داده از فرزند به والد دارید، باید از مکانیزم‌های دیگری مانند callbacks استفاده کنید.

Context یک قرارداد یک‌طرفه است؛ استفاده از آن برای ارتباط دوطرفه، معماری بلاک را پیچیده می‌کند.

عیب‌یابی مشکلات Context

عیب‌یابی مسائل مربوط به Block Context نیازمند رویکردی سیستماتیک است. در ادامه به بررسی مراحل عیب‌یابی می‌پردازیم.

مرحله اول: بررسی block.json

اولین گام، بررسی صحت تعریف providesContext و usesContext در فایل‌های block.json است. مطمئن شوید که نام Contextها با یکدیگر مطابقت دارند و نام Attributeها در بلاک والد صحیح هستند.

مرحله دوم: بررسی ساختار درختی بلاک

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

مرحله سوم: بررسی کنسول مرورگر

گام سوم، بررسی کنسول مرورگر است. گوتنبرگ پیام‌های خطای دقیقی درباره Contextهای نامعتبر در کنسول نمایش می‌دهد. به‌دنبال پیام‌هایی مانند «Invalid context key» بگردید.

مرحله چهارم: تست با مقادیر مختلف

گام چهارم، تست با مقادیر مختلف Context است. مقادیر مختلف را در بلاک والد تنظیم کنید و بررسی کنید که آیا بلاک فرزند به‌درستی به‌روزرسانی می‌شود.

مرحله پنجم: بررسی سازگاری با Contextهای هسته

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

پرسش‌های پرتکرار درباره Block Context

Block Context چیست و چه تفاوتی با Attributes دارد؟

Block Context یک کانال ارتباطی از والد به فرزند است که داده را در زمان رندر منتقل می‌کند، در حالی که Attributes داده‌های دائمی بلاک هستند که در پایگاه داده ذخیره می‌شوند.

چگونه می‌توانم یک Context جدید تعریف کنم؟

در بلاک والد، کلید providesContext را با نام Context و نام Attribute مربوطه تعریف کنید. در بلاک فرزند، نام Context را در آرایه usesContext قرار دهید. برای مطالعه بیشتر، مقاله Block Attributes در وردپرس چیست و مدیریت داده‌های بلاک چطور انجام می‌شود؟ را ببینید.

آیا Context از چند سطح عبور می‌کند؟

بله، Context از تمام سطوح عبور می‌کند و بلاک‌های نوه نیز می‌توانند به آن دسترسی داشته باشند، مشروط بر آنکه نام Context در usesContext آن‌ها ذکر شده باشد.

آیا می‌توانم Context را در سطح میانی تغییر دهم؟

بله، با تعریف مجدد providesContext در بلاک میانی، می‌توانید مقدار Context را برای فرزندان خود تغییر دهید.

چگونه به Context در PHP دسترسی پیدا کنم؟

در render_callback، پارامتر سوم یک شیء WP_Block است که پراپرتی context دارد. همچنین می‌توانید از تابع get_block_context استفاده کنید.

آیا Context بر سرعت سایت تأثیر می‌گذارد؟

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

آیا می‌توانم Context را در بلاک‌های Synced استفاده کنم؟

بلاک‌های Synced نیز از Context پشتیبانی می‌کنند، اما توجه داشته باشید که Context در الگوهای همگام به‌صورت مشترک مدیریت می‌شود. برای مطالعه بیشتر، مقاله Synced Patterns در وردپرس چطور محتوا را در چند صفحه هماهنگ می‌کند؟ را ببینید.

چه اتفاقی می‌افتد اگر Context در دسترس نباشد؟

اگر Context در دسترس نباشد، مقدار آن undefined خواهد بود. برای جلوگیری از خطا، همیشه مقدار پیش‌فرض مناسب تعریف کنید.

آیا Context از TypeScript پشتیبانی می‌کند؟

بله، پکیج‌های گوتنبرگ دارای type definition برای Context هستند و می‌توانید از آن‌ها در پروژه‌های TypeScript استفاده کنید. برای مطالعه بیشتر، مقاله چرا TypeScript برای توسعه دهندگان جاوااسکریپت یک تحول بنیادی است؟ را ببینید.

آیا می‌توانم Context را از سمت فرزند به والد ارسال کنم؟

خیر، Context یک کانال یک‌طرفه از والد به فرزند است. برای ارتباط دوطرفه، باید از مکانیزم‌های دیگری مانند callbacks استفاده کنید.

آیا Context در بلاک‌های هسته وردپرس قابل تغییر است؟

بله، با استفاده از فیلترها مانند block_type_metadata_settings می‌توانید Contextهای بلاک‌های هسته را تغییر دهید.

آیا Context بر سئو تأثیر می‌گذارد؟

Context به‌طور مستقیم بر سئو تأثیر نمی‌گذارد، اما داده‌هایی که در خروجی HTML بازتاب می‌یابند، می‌توانند بر سئو اثرگذار باشند. برای مطالعه بیشتر، مقاله Block Supports در وردپرس چیست و چطور امکانات بلاک را کنترل کنیم؟ را ببینید.

چگونه می‌توانم Context را با Contextهای هسته هماهنگ کنم؟

Contextهای هسته وردپرس شامل postId، postType، queryId، query و isStackedOnMobile هستند. با افزودن این نام‌ها به usesContext بلاک فرزند، می‌توانید از آن‌ها بهره‌مند شوید.

آیا Context در بلاک‌های Dynamic نیز کار می‌کند؟

بله، Context در Dynamic Blocks از طریق پارامتر سوم render_callback قابل دسترسی است.

آیا می‌توانم Context را در ویرایشگر و فرانت‌اند متفاوت مدیریت کنم؟

Context در ویرایشگر و فرانت‌اند از یک قرارداد واحد پیروی می‌کند، اما در ویرایشگر از طریق Data API و در فرانت‌اند از طریق ساختار سراسری مدیریت می‌شود. می‌توانید از فیلترها برای تغییر رفتار در هر محیط استفاده کنید.

نگاه مهندسی به لایه Context

از منظر معماری نرم‌افزار، Block Context یک پیاده‌سازی از الگوی Dependency Injection در سطح بلاک است. در این الگو، بلاک فرزند به یک قرارداد مشخص (نام Context) وابسته است، نه به یک والد خاص. این جداسازی، امکان استفاده مجدد از بلاک فرزند را در چندین والد مختلف فراهم می‌آورد و وابستگی‌های مستقیم را کاهش می‌دهد. برای مطالعه بیشتر درباره TypeScript، مقاله تعریف Interface حرفه‌ای برای آبجکت‌ها در TypeScript را ببینید.

یکی از جنبه‌های کمتر شناخته‌شده، نحوه تعامل Context با مکانیزم سریالایز است. Context در زمان ذخیره‌سازی، به‌صورت مستقیم در JSON سریالایز ذخیره نمی‌شود، بلکه در زمان بارگذاری از ساختار درختی بلاک‌ها استخراج می‌گردد. این رویکرد باعث می‌شود که Context در صورت تغییر ساختار، به‌درستی بازسازی شود. برای مطالعه بیشتر، مقاله Block Serialization در وردپرس چیست و ذخیره بلاک‌ها چطور کار می‌کند؟ را ببینید.

از منظر عملکرد، Context یک trade-off میان سادگی کد و مصرف حافظه ایجاد می‌کند. استفاده از Context، کد را ساده‌تر و قابل نگهداری‌تر می‌کند، اما هر Context اضافه، یک ساختار داده اضافه در حافظه ویرایشگر ایجاد می‌کند. برای بهینه‌سازی، تنها Contextهای ضروری را تعریف کنید.

از منظر امنیت، Context یک لایه ایمن محسوب می‌شود زیرا تنها از والد به فرزند منتقل می‌شود و امکان دستکاری آن از سمت فرزند وجود ندارد. این ویژگی از بروز آسیب‌پذیری‌های احتمالی جلوگیری می‌کند.

از منظر قابلیت نگهداری، Context یک سرمایه‌گذاری بلندمدت است. با تعریف Contextهای استاندارد و مستندسازی آن‌ها، می‌توانید بلاک‌هایی بسازید که در چندین پروژه قابل استفاده مجدد باشند. این رویکرد مشابه طراحی APIهای عمومی است که در بلندمدت از هزینه‌های نگهداری می‌کاهد.

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

از منظر مقیاس‌پذیری، Context امکان استفاده مجدد از بلاک‌های فرزند را در چندین بلاک والد فراهم می‌کند. یک بلاک فرزند که از Context استفاده می‌کند، می‌تواند در Query Loop، Columns، Navigation و هر بلاک دیگری که Context مربوطه را ارائه می‌دهد، کار کند. برای مطالعه بیشتر درباره معماری بلاک‌ها، مقاله Static یا Dynamic Blocks در وردپرس؛ کدام انتخاب درست است؟ را ببینید.

آینده Block Context

تیم هسته وردپرس در حال کار بر روی گسترش Block Context است. از جمله تحولات مورد انتظار می‌توان به پشتیبانی از Contextهای داینامیک که از REST API یا سایر منابع داده تغذیه می‌شوند، بهبود عملکرد در ساختارهای عمیقاً تودرتو و ادغام بهتر با Block Bindings API اشاره کرد. Block Bindings API که در حال توسعه است، امکان اتصال Context به منابع داده خارجی را فراهم می‌کند و این امر می‌تواند نقش Context را در معماری بلاک‌ها متحول کند.

نکات کلیدی

  • Block Context یک مکانیزم رسمی برای اشتراک داده از بلاک والد به بلاک‌های فرزند است.
  • این مکانیزم از دو کلید providesContext و usesContext در فایل block.json استفاده می‌کند.
  • Context از تمام سطوح عبور می‌کند و بلاک‌های نوه نیز می‌توانند به آن دسترسی داشته باشند.
  • در ویرایشگر، Context از طریق پارامتر context در تابع edit قابل دسترسی است.
  • در فرانت‌اند، Context از طریق پارامتر $block->context در render_callback قابل دسترسی است.
  • مقدار پیش‌فرض Context از بروز خطاهای undefined جلوگیری می‌کند.
  • Contextهای هسته وردپرس شامل postId، postType و query هستند.
  • Context یک کانال یک‌طرفه است و برای ارتباط دوطرفه مناسب نیست.
  • تنها Contextهای ضروری را ارائه دهید تا عملکرد حفظ شود.
  • همیشه از namespace مشخص برای نام Context استفاده کنید.
  • Context در زمان ذخیره‌سازی در JSON سریالایز نمی‌شود و در زمان رندر بازسازی می‌گردد.
  • Block Context یک سرمایه‌گذاری بلندمدت برای ساخت بلاک‌های قابل استفاده مجدد است.

اگر در پروژه‌ای از Block Context استفاده کرده‌اید، برایم جالب است بدانید کدام جنبه آن بیشترین تأثیر را بر معماری بلاک شما داشته است؛ به‌ویژه اگر راه‌حل خلاقانه‌ای برای مدیریت Contextهای پیچیده یا ترکیب آن با Contextهای هسته پیدا کرده‌اید که می‌تواند برای دیگران مفید باشد. تجربه خود را در دیدگاه‌ها بنویسید. 🚀