🧩 فصل ۱۲: ساخت بلاک Gutenberg با React
Gutenberg Blocks نسل جدید ویرایشگر وردپرسه. توی این فصل یاد میگیریم چطور بلاکهای اختصاصی بسازیم - از یه بلاک ساده Hello World تا بلاکهای داینامیک.
Gutenberg Blocks نسل جدید ویرایشگر وردپرسه. توی این فصل یاد میگیریم چطور بلاکهای اختصاصی بسازیم – از یه بلاک ساده Hello World تا بلاکهای داینامیک.
🎯 چرا بلاک سفارشی بسازیم؟
بلاکهای پیشفرض گوتنبرگ خوبن، ولی برای کارهای خاص نیاز به بلاک سفارشی داری:
- نمایش دیتای خاص (مثل آخرین محصولات با ساختار اختصاصی)
- UI سفارشی برای کاربر (CTA، تخفیف، شمارنده)
- یکپارچگی با API خارجی
- طراحی شرکتی منحصربهفرد
🛠 ابزارهای مورد نیاز
- Node.js ۱۸+ (برای npm/npx)
- npm یا yarn
- @wordpress/scripts برای build و dev
- یک ادیتور (VSCode پیشنهاد میشه)
سادهترین راه شروع، استفاده از پکیج رسمی @wordpress/create-block هست. با یه دستور همهچیز رو راه میاندازه.
🚀 ساخت اولین بلاک
قدم ۱: ساخت پروژه
# برو به پوشه plugins
cd wp-content/plugins/
# ساخت بلاک
npx @wordpress/create-block my-first-block
# سؤالها رو جواب بده، ساختار خودکار ساخته میشه
cd my-first-block
# نصب وابستگیها (خودکار انجام شده)
# اجرای dev mode
npm start
# build برای production
npm run build
ساختار پروژه
my-first-block/
├── build/ # خروجی build (خودکار)
├── src/
│ ├── block.json # تعریف بلاک
│ ├── edit.js # کامپوننت ویرایشگر
│ ├── save.js # خروجی نهایی
│ ├── style.scss # استایل front
│ ├── editor.scss # استایل editor
│ └── index.js # ثبت بلاک
├── my-first-block.php # plugin file
├── package.json
└── readme.txt
فایل اصلی PHP
<?php
/**
* Plugin Name: اولین بلاک من
* Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) exit;
function create_block_my_first_block_block_init() {
register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'create_block_my_first_block_block_init' );
📋 block.json
مرکز تعریف بلاک. وردپرس از این فایل اطلاعات بلاک رو میخونه.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "icsd/hero-section",
"version": "1.0.0",
"title": "بخش هیرو",
"category": "design",
"icon": "format-image",
"description": "یه بخش هیرو زیبا با عنوان و توضیحات",
"keywords": ["hero", "header", "banner"],
"textdomain": "icsd",
"attributes": {
"title": {
"type": "string",
"default": "عنوان شما اینجا"
},
"subtitle": {
"type": "string",
"default": "توضیح کوتاه"
},
"bgColor": {
"type": "string",
"default": "#2271b1"
}
},
"supports": {
"html": false,
"align": ["wide", "full"]
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
🎯 Attributes – ذخیره دادههای بلاک
هر چی کاربر در بلاک تنظیم میکنه (متن، رنگ، آدرس عکس) بهصورت attribute ذخیره میشه.
edit.js – ویرایشگر
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
RichText,
InspectorControls
} from '@wordpress/block-editor';
import {
PanelBody,
ColorPicker
} from '@wordpress/components';
export default function Edit( { attributes, setAttributes } ) {
const { title, subtitle, bgColor } = attributes;
const blockProps = useBlockProps( {
style: { backgroundColor: bgColor }
} );
return (
<>
{/* پنل تنظیمات سمت راست */}
<InspectorControls>
<PanelBody title="تنظیمات هیرو">
<p>رنگ پسزمینه:</p>
<ColorPicker
color={ bgColor }
onChange={ ( newColor ) =>
setAttributes( { bgColor: newColor } )
}
/>
</PanelBody>
</InspectorControls>
{/* خود بلاک */}
<div { ...blockProps }>
<RichText
tagName="h1"
value={ title }
onChange={ ( newTitle ) =>
setAttributes( { title: newTitle } )
}
placeholder="عنوان..."
/>
<RichText
tagName="p"
value={ subtitle }
onChange={ ( newSubtitle ) =>
setAttributes( { subtitle: newSubtitle } )
}
placeholder="توضیحات..."
/>
</div>
</>
);
}
save.js – خروجی نهایی
import { useBlockProps, RichText } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { title, subtitle, bgColor } = attributes;
const blockProps = useBlockProps.save( {
style: { backgroundColor: bgColor }
} );
return (
<div { ...blockProps }>
<RichText.Content tagName="h1" value={ title } />
<RichText.Content tagName="p" value={ subtitle } />
</div>
);
}
index.js – ثبت بلاک
import { registerBlockType } from '@wordpress/blocks';
import './style.scss';
import Edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType( metadata.name, {
edit: Edit,
save,
} );
⚡ بلاک داینامیک (Dynamic Block)
بلاکهای داینامیک خروجی رو با PHP در زمان render تولید میکنن – برای دادههای متغیر مثل آخرین نوشتهها مفیدن.
تفاوت Static و Dynamic
| Static | Dynamic |
|---|---|
| HTML در save.js تولید میشه | HTML با PHP تولید میشه |
| توی محتوای پست ذخیره میشه | هر بار از نو ساخته میشه |
| سرعت بیشتر (cache) | دادههای بهروز |
| برای محتوای ثابت | برای محتوای زنده |
ساخت بلاک داینامیک
۱. block.json
{
"name": "icsd/recent-products",
"title": "آخرین محصولات",
"category": "widgets",
"render": "file:./render.php",
"attributes": {
"count": {
"type": "number",
"default": 4
}
}
}
۲. save.js (null برای داینامیک)
// برای بلاک داینامیک، save باید null برگردونه
export default function save() {
return null;
}
۳. render.php
<?php
$count = $attributes['count'] ?? 4;
$query = new WP_Query( array(
'post_type' => 'icsd_product',
'posts_per_page' => $count,
'orderby' => 'date',
'order' => 'DESC',
) );
?>
<div <?php echo get_block_wrapper_attributes(); ?>>
<h3>آخرین محصولات</h3>
<div class="products-grid">
<?php while ( $query->have_posts() ) : $query->the_post(); ?>
<article class="product">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail( 'medium' ); ?>
<h4><?php the_title(); ?></h4>
</a>
</article>
<?php endwhile; wp_reset_postdata(); ?>
</div>
</div>
۴. edit.js (ویرایشگر)
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl } from '@wordpress/components';
import ServerSideRender from '@wordpress/server-side-render';
export default function Edit( { attributes, setAttributes } ) {
const { count } = attributes;
return (
<div { ...useBlockProps() }>
<InspectorControls>
<PanelBody title="تنظیمات">
<RangeControl
label="تعداد محصول"
value={ count }
onChange={ ( newCount ) =>
setAttributes( { count: newCount } )
}
min={ 1 }
max={ 12 }
/>
</PanelBody>
</InspectorControls>
{/* پیشنمایش زنده */}
<ServerSideRender
block="icsd/recent-products"
attributes={ attributes }
/>
</div>
);
}
این کامپوننت پیشنمایش زنده از خروجی PHP رو در ادیتور نشون میده. هر بار که attribute تغییر کنه، خروجی بهروز میشه.
🎯 مثال کامل: بلاک شمارنده
بلاک شمارنده آماری (Counter)
یه بلاک که عددی نمایش میده با لیبل (مثل تعداد مشتری، پروژه و…)
import { useBlockProps, RichText, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';
export default function Edit( { attributes, setAttributes } ) {
const { number, label, icon } = attributes;
return (
<>
<InspectorControls>
<PanelBody title="تنظیمات شمارنده">
<TextControl
label="آیکن (ایموجی)"
value={ icon }
onChange={ ( v ) => setAttributes( { icon: v } ) }
/>
</PanelBody>
</InspectorControls>
<div { ...useBlockProps( { className: 'counter-block' } ) }>
<div className="counter-icon">{ icon }</div>
<RichText
tagName="span"
className="counter-number"
value={ number }
onChange={ ( v ) => setAttributes( { number: v } ) }
placeholder="۱۰۰+"
/>
<RichText
tagName="span"
className="counter-label"
value={ label }
onChange={ ( v ) => setAttributes( { label: v } ) }
placeholder="مشتری راضی"
/>
</div>
</>
);
}
📦 ساخت Block Patterns
Pattern یعنی ترکیب چند بلاک از پیش آماده. کاربر فقط روی pattern کلیک میکنه و همه بلاکها وارد صفحه میشن.
function icsd_register_block_patterns() {
register_block_pattern(
'icsd/hero-with-cta',
array(
'title' => 'هیرو با CTA',
'description' => 'بخش هیرو با دکمه فراخوان عمل',
'categories' => array( 'icsd' ),
'content' => '
<!-- wp:cover -->
<div class="wp-block-cover">
<!-- wp:heading {"level":1} -->
<h1>با ICSD همراه شوید</h1>
<!-- /wp:heading -->
</div>
<!-- /wp:cover -->
',
)
);
}
add_action( 'init', 'icsd_register_block_patterns' );
📝 خلاصه فصل
- برای ساخت بلاک:
npx @wordpress/create-block - block.json مرکز تعریف بلاکه
- edit.js → ویرایشگر، save.js → خروجی
- برای دادههای زنده، بلاک Dynamic بساز
- InspectorControls → پنل سایدبار
- RichText → ویرایش متن
- ServerSideRender → پیشنمایش PHP در ویرایشگر