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

ویجت در وردپرس: کلاسیک و بلاک

وردپرس از نسخهٔ ۵.۸ به بعد، دو نوع ویجت دارد: یک — ویجت کلاسیک. بر پایهٔ کلاس WP_Widget و PHP. در پنل «نمایش ← ویجت‌ها» مدیریت می‌شود. دو — ویجت بلاکی. بر پایهٔ بلاک‌های گوتنبرگ. در ویرایشگر بلاکی، داخل نواحی ویجت قابل افزودن است. تفاوت اصلی در رابط مدیریت است؛ خروجی نهایی در front-end یکسان است. برای تصمیم‌گیری، سه سؤال: یک — آیا سایت شما از ویرایشگر بلاک در ویجت‌ها استفاده می‌کند؟ اگر بله، ویجت بلاکی انتخاب اول است. دو — آیا تنظیمات ویجت شامل فیلدهای پیچیده است (تصویر، تکرارشونده)؟ اگر بله، بلاکی انعطاف بیشتری می‌دهد. سه — آیا پروژه شما با محدودیت نسخهٔ وردپرس قدیمی سر و کار دارد؟ اگر بله، ویجت کلاسیک سازگارتر است. تصویر کامل گوتنبرگ در گوتنبرگ و آیندهٔ ویرایش محتوا و ساخت بلوک سفارشی گوتنبرگ آمده است.

ویجت، مثل بلوکی از لِگو در قالب است؛ می‌توانید بدون تغییر قالب، بخش‌های تازه‌ای به سایت اضافه کنید — به شرطی که قالب، نواحی مناسب را برای این کار فراهم کرده باشد.

کجا ویجت اختصاصی بنویسیم؟

سه مسیر ممکن: یک — افزونهٔ اختصاصی. انتخاب درست برای هر ویجتی که باید سال‌ها بماند. راهنمای کامل در کدنویسی اختصاصی افزونه و توسعهٔ افزونه از صفر. دو — mu-plugins. برای ویجت زیرساختی که نباید کاربر خاموشش کند. سه — چایلد تم. فقط اگر ویجت کاملاً به قالب وابسته است و در روز تغییر قالب از دست رفتنش قابل قبول است. ساختار پوشه‌ها در ساختار فایل‌های افزونهٔ استاندارد. قاعدهٔ عملی: هر ویجتی که تنظیمات دارد یا به داده‌ای متصل است، در افزونه. اگر ویجت فقط یک قطعهٔ HTML ثابت است، چایلد تم می‌تواند کافی باشد.

ویجت کلاسیک با WP_Widget

ساخت ویجت کلاسیک، با ارث‌بری از کلاس WP_Widget انجام می‌شود:

class My_Contact_Widget extends WP_Widget {

    public function __construct() {
        parent::__construct(
            'my_contact_widget',
            'ویجت تماس سریع',
            array(
                'description' => 'نمایش شمارهٔ تماس و واتس‌اپ',
                'classname'   => 'widget-my-contact',
            )
        );
    }
    // متدهای form، update و widget در ادامه می‌آیند.
}

ساختار __construct سه بخش دارد: یک — id_base: شناسهٔ یکتای ویجت، همیشه با پیشوند افزونه. دو — نام نمایشی: عنوانی که در پنل ویجت‌ها دیده می‌شود. سه — description: توضیح کوتاه. نکته: id_base فقط با حروف کوچک لاتین و زیرخط نوشته می‌شود. الگوی نام‌گذاری مطابق استانداردهای کدنویسی وردپرس. یک اشتباه رایج: استفاده از id_base کوتاه و عمومی مثل contact که با افزونه‌های دیگر تعارض می‌کند. همیشه پیشوند اختصاصی بگذارید.

متد form و ذخیرهٔ تنظیمات

متد form، رابط کاربری ویجت در پیشخوان را می‌سازد:

public function form( $instance ) {
    $title   = isset( $instance['title'] )   ? $instance['title']   : 'تماس سریع';
    $phone   = isset( $instance['phone'] )   ? $instance['phone']   : '';
    $whatsapp = isset( $instance['whatsapp'] ) ? $instance['whatsapp'] : '';
    ?>
    <p>
        <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
            عنوان:
        </label>
        <input
            class="widefat"
            id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
            name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
            type="text"
            value="<?php echo esc_attr( $title ); ?>" />
    </p>
    <p>
        <label for="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>">
            شمارهٔ تماس:
        </label>
        <input
            class="widefat"
            id="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>"
            name="<?php echo esc_attr( $this->get_field_name( 'phone' ) ); ?>"
            type="tel"
            dir="ltr"
            value="<?php echo esc_attr( $phone ); ?>" />
    </p>
    <?php
}

سه نکته: یک — get_field_id و get_field_name: هرگز نام فیلد را دستی نسازید. این دو متد، شناسهٔ یکتا با پیشوند ویجت می‌سازند. دو — esc_attr: روی تمام مقادیر خروجی، الزامی است. سه — isset برای مقادیر: در اولین نمایش ویجت، $instance خالی است؛ مقادیر پیش‌فرض را در متغیرهای جدا تنظیم کنید. راهنمای کامل در PHP امن در وردپرس و پاک‌سازی داده‌ها.

متد update و اعتبارسنجی

متد update، هنگام ذخیرهٔ تنظیمات اجرا می‌شود و وظیفهٔ اعتبارسنجی ورودی را دارد:

public function update( $new_instance, $old_instance ) {
    $instance = $old_instance;

    $instance['title']    = sanitize_text_field( $new_instance['title'] );
    $instance['phone']    = sanitize_text_field( $new_instance['phone'] );
    $instance['whatsapp'] = esc_url_raw( $new_instance['whatsapp'] );

    return $instance;
}

دو نکته: یک — پاک‌سازی هر فیلد بسته به نوع. sanitize_text_field برای متن، esc_url_raw برای URL، absint برای عدد، sanitize_email برای ایمیل. دو — بازگرداندن آرایهٔ instance. اگر $new_instance خالی باشد، مقادیر قبلی حفظ می‌شوند. راهنمای دقیق در اعتبارسنجی داده‌ها و پاک‌سازی داده‌ها. یک اشتباه شایع: نبود update یا خالی گذاشتنش، که باعث می‌شود هر ورودی کاربر بدون فیلتر ذخیره شود و خطر امنیتی جدی بسازد.

متد widget و رندر خروجی

متد widget، خروجی front-end ویجت را می‌سازد:

public function widget( $args, $instance ) {
    echo $args['before_widget'];

    if ( ! empty( $instance['title'] ) ) {
        echo $args['before_title'];
        echo esc_html( $instance['title'] );
        echo $args['after_title'];
    }

    echo '<div class="my-contact-widget">';
    if ( ! empty( $instance['phone'] ) ) {
        echo '<a href="tel:' . esc_attr( $instance['phone'] ) . '" class="contact-phone">';
        echo esc_html( $instance['phone'] );
        echo '</a>';
    }
    if ( ! empty( $instance['whatsapp'] ) ) {
        echo '<a href="' . esc_url( $instance['whatsapp'] ) . '" class="contact-whatsapp">واتس‌اپ</a>';
    }
    echo '</div>';

    echo $args['after_widget'];
}

نکتهٔ مهم: before_widget و after_widget، لایه‌بندی قالب را تضمین می‌کنند. اگر آن‌ها را چاپ نکنید، ویجت شما با سایر ویجت‌های قالب هم‌شکل نمی‌شود. الگوی مشابه در ساخت شورت‌کد. یک قاعده: هرگز از print_r یا var_dump در خروجی front-end استفاده نکنید — امکان افشای داده.

ثبت ویجت با register_widget

پس از تعریف کلاس، ویجت را با register_widget به وردپرس معرفی می‌کنیم:

add_action( 'widgets_init', function() {
    register_widget( 'My_Contact_Widget' );
} );

نکته: هوک widgets_init، نه init. اگر روی init ثبت کنید، ممکن است ویجت در پنل نمایش داده نشود. راهنمای هوک‌ها در هوک‌های وردپرس، تفاوت اکشن و فیلتر، و نحوهٔ استفاده از add_action. پس از ثبت، ویجت شما در پنل «نمایش ← ویجت‌ها» ظاهر می‌شود.

ثبت نواحی ویجت در قالب

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

function my_theme_widgets_init() {
    register_sidebar( array(
        'name'          => 'سایدبار اصلی',
        'id'            => 'sidebar-primary',
        'description'   => 'نوار کناری اصلی سایت',
        'before_widget' => '<section id="%1$s" class="widget %2$s">',
        'after_widget'  => '</section>',
        'before_title'  => '<h3 class="widget-title">',
        'after_title'   => '</h3>',
    ) );

    register_sidebar( array(
        'name'          => 'فوتر ستون اول',
        'id'            => 'footer-1',
        'before_widget' => '<div id="%1$s" class="footer-widget %2$s">',
        'after_widget'  => '</div>',
        'before_title'  => '<h4 class="footer-widget-title">',
        'after_title'   => '</h4>',
    ) );
}
add_action( 'widgets_init', 'my_theme_widgets_init' );

سپس در فایل template:

<?php if ( is_active_sidebar( 'sidebar-primary' ) ) : ?>
    <aside class="sidebar">
        <?php dynamic_sidebar( 'sidebar-primary' ); ?>
    </aside>
<?php endif; ?>

نکته: is_active_sidebar جلوی رندر کردن نواحی خالی را می‌گیرد. بدون آن، ظرف خالی با CSS margin نمایش داده می‌شود. الگوی کامل قالب در ساختار فایل‌های قالب استاندارد و توسعهٔ قالب از صفر.

ویجت بلاکی مدرن

از وردپرس ۵.۸ به بعد، ویجت‌ها می‌توانند به‌صورت بلاکی ساخته شوند. روش با JavaScript و React است و مزیتش این است که در ویرایشگر بلاکی داخل نواحی ویجت هم استفاده می‌شود. ساختار پایه:

// در فایل JS ثبت بلاک
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'my-plugin/contact-widget', {
    title: 'تماس سریع',
    icon: 'phone',
    category: 'widgets',
    edit: () => {
        const blockProps = useBlockProps();
        return <div {...blockProps}>تماس سریع</div>;
    },
    save: () => {
        const blockProps = useBlockProps.save();
        return <div {...blockProps}>تماس سریع</div>;
    },
} );

نکته: برای ساخت بلاک ویجت، نیاز به ابزار @wordpress/scripts و بیلد JavaScript دارید. راهنمای کامل در ساخت بلوک سفارشی گوتنبرگ. در پروژه‌های جدید، ترکیب ویجت کلاسیک (برای منطق پویا) و ویجت بلاکی (برای UI انعطاف‌پذیر) می‌تواند کارآمد باشد. تصمیم بر اساس نیاز و اندازهٔ تیم گرفته می‌شود: تیم‌های فنی که با React راحت‌اند، بلاکی را ترجیح می‌دهند؛ تیم‌های کوچک‌تر، کلاسیک را.

امنیت در ویجت اختصاصی

سه قاعدهٔ الزامی: یک — پاک‌سازی در متد update. هر فیلد بسته به نوع خود. دو — escape در متد widget. esc_html، esc_attr، esc_url. سه — نبود دسترسی مستقیم به فایل. در ابتدای فایل PHP، خط if ( ! defined( 'ABSPATH' ) ) exit;. راهنمای کامل در PHP امن در وردپرس، پاک‌سازی داده‌ها، و امنیت پروژه وردپرس. اصول کلی امنیت در امنیت وردپرس برای مبتدیان. یک آسیب‌پذیری شایع در ویجت‌های اختصاصی: نبود escape در متد widget، که به XSS اجازه می‌دهد از طریق تنظیمات ویجت تزریق شود. حتی اگر کاربر تنها ادمین است، هنوز باید escape رعایت شود چون برخی تنظیمات ممکن است از منابع غیرمستقیم بیاید.

الگوهای ساختاری پیشرفته

سه الگوی ساختاری در ویجت اختصاصی حرفه‌ای: یک — جداسازی منطق از نمایش. کلاس ویجت باید نازک باشد؛ منطق سنگین را در کلاس جداگانه قرار دهید. الگو:

class My_Contact_Data {
    public static function get_contact_info() {
        return array(
            'phone'    => get_option( 'my_phone' ),
            'whatsapp' => get_option( 'my_whatsapp' ),
        );
    }
}

class My_Contact_Widget extends WP_Widget {
    public function widget( $args, $instance ) {
        $data = My_Contact_Data::get_contact_info();
        // رندر بر اساس $data
    }
}

مزیت: تست‌پذیری و نگهداری ساده‌تر. دو — کش داده‌های سنگین. اگر ویجت، داده از سرویس بیرونی می‌خواند، با transients کش کنید:

$cache = get_transient( 'my_widget_data' );
if ( $cache === false ) {
    $cache = /* درخواست بیرونی */;
    set_transient( 'my_widget_data', $cache, HOUR_IN_SECONDS );
}

راهنمای کامل در ترنزینت‌ها در وردپرس و اتصال وردپرس به سرویس‌های خارجی. سه — ثبت در ساختار پوشه‌ای افزونه. ویجت را در پوشهٔ includes/ یا widgets/ نگه دارید و در فایل اصلی افزونه، بارگذاری کنید. الگو در ساختار فایل‌های افزونهٔ استاندارد. یک نکتهٔ ساختاری از تجربه‌های میدانی: در پروژه‌ای با دوازده ویجت اختصاصی، تنها ویجت‌هایی که منطق را از نمایش جدا کرده بودند، در آپدیت‌های بعدی وردپرس بدون مشکل ادامه دادند. بقیه، در هر آپدیت، بازبینی دستی لازم داشتند. این تفاوت ساختاری، هزینهٔ نگهداری سه‌ساله را تعیین می‌کند. الگوهای کدنویسی امن و حرفه‌ای در کدنویسی اختصاصی افزونه و بهینه‌سازی کد وردپرس آمده است.

اشتباهات رایج

  • ثبت ویجت روی هوک اشتباه: باید widgets_init باشد، نه init. هوک‌های وردپرس.
  • نبود ABSPATH در ابتدای فایل: خطر دسترسی مستقیم. PHP امن.
  • نبود پاک‌سازی در update: ذخیرهٔ ورودی خام و خطر XSS. پاک‌سازی داده‌ها.
  • نبود escape در widget: خطر XSS در front-end. اعتبارسنجی داده‌ها.
  • استفاده از id_base عمومی: تعارض با افزونه‌های دیگر. اشتباهات رایج توسعه.
  • نبود before_widget و after_widget در خروجی: ناهماهنگی با سایر ویجت‌ها.
  • ثبت ویجت در فایل قالب والد: با آپدیت قالب از دست می‌رود. چایلد تم.
  • نبود کش در داده‌های سنگین: کندی سایت. ترنزینت‌ها.
  • نبود is_active_sidebar در قالب: نمایش ظرف خالی. ساختار قالب.
  • نادیده‌گرفتن ویجت بلاکی در پروژه‌های مدرن: فرصت از دست رفته برای UX بهتر. گوتنبرگ.

ساخت ویجت اختصاصی در وردپرس، مسیری روشن دارد: تعریف کلاس با ارث‌بری از WP_Widget، پیاده‌سازی سه متد form، update، و widget، ثبت با register_widget روی هوک widgets_init، و رعایت اصول امنیت در هر دو نقطهٔ ورودی و خروجی. در پروژه‌های مدرن، ویجت بلاکی جایگاه مکمل دارد. اگر امروز یک کار در این مسیر انجام می‌دهید: یک ویجت سادهٔ تماس سریع بسازید و آن را در سایدبار قالب نصب کنید. همان اولین رندر، درهای تازه‌ای به سفارشی‌سازی بدون دست‌زدن به قالب باز می‌کند. اگر تجربه‌ای از یک ویجت اختصاصی دارید که در بلندمدت مفید یا پرمشکل بوده، در دیدگاه‌ها بنویسید — همان گزارش‌های واقعی، این راهنما را دقیق‌تر می‌کند. 🧩