مدیریت ویجت‌ها در قالب‌های مدرن (Widget Management in Modern Themes) یکی از مهارت‌های پایه‌ای است که هر توسعه‌دهنده‌ی وردپرس برای ساخت قالب‌های حرفه‌ای باید بر آن مسلط باشد. ویجت‌ها، از نسخه‌های اولیه‌ی وردپرس وجود داشته‌اند و هنوز هم یکی از پرکاربردترین ابزارها برای افزودن محتوای پویا به بخش‌های مختلف سایت هستند. اما با معرفی ویرایشگر بلاکی (Block Editor) و تغییرات بنیادین در معماری قالب‌های مدرن، نحوه‌ی مدیریت ویجت‌ها نیز دستخوش تحول شده است. در این راهنما، از تعریف پایه‌ای شروع می‌کنیم و به‌تدریج به مباحث پیشرفته‌تر مثل ثبت سایدبار، ساخت ویجت سفارشی، مهاجرت از کلاسیک به بلاک و بهینه‌سازی کارایی می‌رسیم.

ویجت‌ها در وردپرس، قطعه‌های مستقل از محتوا هستند که در مناطق مشخصی از قالب (Sidebar) نمایش داده می‌شوند. این مناطق، معمولاً در نوار کناری، فوتر یا بخش‌های خاصی از صفحه قرار دارند و به کاربر اجازه می‌دهند بدون نیاز به کدنویسی، محتوای پویا اضافه کند. با معرفی ویرایشگر بلاکی در وردپرس ۵.۸، ویجت‌ها نیز به بلاک تبدیل شدند و این تحول، هم فرصت‌های جدیدی ایجاد کرد و هم چالش‌هایی برای توسعه‌دهندگان قالب.

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

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

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

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

ویجت و منطقه ویجت دقیقاً چیست؟

ویجت (Widget) در وردپرس یک قطعه‌ی مستقل از محتوا است که در مناطق مشخصی از قالب نمایش داده می‌شود. این مناطق، با نام سایدبار (Sidebar) شناخته می‌شوند، اما برخلاف نامشان، فقط به نوار کناری محدود نمی‌شوند و می‌توانند در فوتر، هدر یا هر جای دیگری از قالب قرار گیرند. مفهوم Web Widget در ویکی‌پدیا توضیح داده شده و ریشه‌ی آن به سیستم‌های مدیریت محتوای اولیه بازمی‌گردد.

هر ویجت، یک کلاس PHP است که از کلاس پایه‌ی WP_Widget ارث‌بری می‌کند. این کلاس، چند متد اصلی دارد که هر کدام مسئول یک بخش از رفتار ویجت هستند. متد __construct() تنظیمات اولیه را تعریف می‌کند. متد widget() خروجی فرانت‌اند را تولید می‌کند. متد form() فرم تنظیمات در پنل مدیریت را می‌سازد. متد update() مقادیر فرم را ذخیره می‌کند.

class My_Widget extends WP_Widget {
    public function __construct() {
        parent::__construct(
            'my_widget',
            __('ویجت من', 'my-theme'),
            ['description' => __('یک ویجت نمونه', 'my-theme')]
        );
    }

    public function widget($args, $instance) {
        echo $args['before_widget'];
        echo $args['before_title'] . esc_html($instance['title']) . $args['after_title'];
        echo '<p>' . esc_html($instance['message']) . '</p>';
        echo $args['after_widget'];
    }

    public function form($instance) {
        // فرم تنظیمات
    }

    public function update($new_instance, $old_instance) {
        // ذخیره تنظیمات
    }
}

منطقه‌ی ویجت (Widget Area) یا سایدبار، یک ناحیه‌ی ثبت‌شده در قالب است که می‌تواند چند ویجت را در خود جای دهد. هر منطقه، با تابع register_sidebar() ثبت می‌شود و دارای یک شناسه‌ی یکتا، نام نمایشی و چند پارامتر ظاهری است. در فرانت‌اند، این منطقه با dynamic_sidebar() فراخوانی می‌شود.

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

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

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

ثبت سایدبار در قالب‌های کلاسیک

ثبت سایدبار در قالب‌های کلاسیک، از طریق تابع register_sidebar() انجام می‌شود. این تابع، معمولاً در هوک widgets_init فراخوانی می‌شود تا اطمینان حاصل شود که وردپرس آماده‌ی ثبت مناطق ویجت است.

add_action('widgets_init', function() {
    register_sidebar([
        'name' => __('سایدبار اصلی', 'my-theme'),
        'id' => 'sidebar-main',
        'description' => __('سایدبار پیش‌فرض قالب', 'my-theme'),
        'before_widget' => '<div id="%1$s" class="widget %2$s">',
        'after_widget' => '</div>',
        'before_title' => '<h3 class="widget-title">',
        'after_title' => '</h3>',
    ]);
});

پارامترهای before_widget، after_widget، before_title و after_title ظاهر ویجت را در فرانت‌اند کنترل می‌کنند. در before_widget، از %1$s برای شناسه‌ی ویجت و از %2$s برای کلاس‌های CSS استفاده می‌شود. این شناسه و کلاس‌ها، به‌طور خودکار توسط وردپرس تولید می‌شوند و امکان استایل‌دهی دقیق را فراهم می‌کنند.

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

<aside class="sidebar">
    <?php if (is_active_sidebar('sidebar-main')) : ?>
        <?php dynamic_sidebar('sidebar-main'); ?>
    <?php else : ?>
        <p><?php esc_html_e('هیچ ویجتی ثبت نشده است.', 'my-theme'); ?></p>
    <?php endif; ?>
</aside>

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

add_action('widgets_init', function() {
    $sidebars = [
        'sidebar-main' => __('سایدبار اصلی', 'my-theme'),
        'sidebar-footer' => __('فوتر', 'my-theme'),
        'sidebar-shop' => __('فروشگاه', 'my-theme'),
    ];

    foreach ($sidebars as $id => $name) {
        register_sidebar([
            'name' => $name,
            'id' => $id,
            'before_widget' => '<div id="%1$s" class="widget %2$s">',
            'after_widget' => '</div>',
            'before_title' => '<h3 class="widget-title">',
            'after_title' => '</h3>',
        ]);
    }
});

این الگو، در پروژه‌های بزرگ بسیار کاربردی است و از تکرار کد جلوگیری می‌کند. برای درک عمیق‌تر نحوه‌ی کار با هوک‌ها در قالب، مقاله هوک‌های وردپرس: قلب تپنده توسعه را مطالعه کنید.

ویجت‌های بلاکی و تحول معماری

با معرفی ویرایشگر بلاکی در وردپرس ۵.۸، ویجت‌ها نیز به بلاک تبدیل شدند. این تحول، یک تغییر بنیادین در معماری ویجت‌ها ایجاد کرد. در رویکرد جدید، هر ویجت یک بلاک است که در ویرایشگر بلاکی قابل ویرایش و تنظیم است. این تغییر، چند مزیت مهم دارد: یکپارچگی تجربه‌ی کاربری، امکان استفاده از همان بلاک‌ها در ویجت و محتوا، و آینده‌ی پایدارتر.

با این حال، این تحول چالش‌هایی نیز ایجاد کرده است. بسیاری از ویجت‌های کلاسیک که توسط افزونه‌ها و قالب‌های قدیمی ارائه شده‌اند، هنوز به بلاک تبدیل نشده‌اند. وردپرس برای حل این مشکل، یک بلاک ویژه به نام «ویجت کلاسیک» (Legacy Widget) ارائه کرده است که امکان استفاده از ویجت‌های قدیمی در ویرایشگر بلاکی را فراهم می‌کند.

// غیرفعال کردن ویرایشگر بلاکی ویجت‌ها و بازگشت به ویجت‌های کلاسیک
add_filter('gutenberg_use_widgets_block_editor', '__return_false');
add_filter('use_widgets_block_editor', '__return_false');

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

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

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

ثبت سایدبار برای ویجت‌های بلاکی

ثبت سایدبار برای ویجت‌های بلاکی، تفاوت چندانی با ثبت سایدبار کلاسیک ندارد. همان تابع register_sidebar() استفاده می‌شود، اما پارامتر before_widget و after_widget ممکن است تأثیر کمتری داشته باشند، زیرا بلاک‌ها معمولاً ساختار HTML خود را دارند.

add_action('widgets_init', function() {
    register_sidebar([
        'name' => __('سایدبار بلاکی', 'my-theme'),
        'id' => 'sidebar-block',
        'description' => __('سایدبار سازگار با ویرایشگر بلاکی', 'my-theme'),
        'before_widget' => '<div id="%1$s" class="widget %2$s">',
        'after_widget' => '</div>',
        'before_title' => '<h3 class="widget-title">',
        'after_title' => '</h3>',
    ]);
});

در فرانت‌اند، همان تابع dynamic_sidebar() استفاده می‌شود. اما یک تفاوت مهم وجود دارد: بلاک‌ها می‌توانند استایل‌های خود را داشته باشند که ممکن است با استایل‌های قالب تداخل کند. برای جلوگیری از این تداخل، می‌توانید از بلاک‌های هسته‌ی وردپرس که از استایل‌های پیش‌فرض استفاده می‌کنند، بهره ببرید یا استایل‌های سفارشی را با دقت بیشتری تعریف کنید.

نکته‌ی مهم در ثبت سایدبار بلاکی، توجه به پشتیبانی از ویژگی‌های بلاکی مثل align-wide و align-full است. اگر قالب شما از این ویژگی‌ها پشتیبانی می‌کند، باید در سایدبار نیز این پشتیبانی را فراهم کنید.

add_theme_support('align-wide');
add_theme_support('responsive-embeds');

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

ساخت ویجت سفارشی با کلاس PHP

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

class My_Recent_Posts_Widget extends WP_Widget {
    public function __construct() {
        parent::__construct(
            'my_recent_posts',
            __('آخرین نوشته‌ها', 'my-theme'),
            ['description' => __('نمایش آخرین نوشته‌ها', 'my-theme')]
        );
    }

    public function widget($args, $instance) {
        $title = !empty($instance['title']) ? $instance['title'] : __('آخرین نوشته‌ها', 'my-theme');
        $count = !empty($instance['count']) ? absint($instance['count']) : 5;

        echo $args['before_widget'];
        echo $args['before_title'] . esc_html($title) . $args['after_title'];

        $query = new WP_Query([
            'posts_per_page' => $count,
            'no_found_rows' => true,
            'ignore_sticky_posts' => true,
        ]);

        if ($query->have_posts()) {
            echo '<ul>';
            while ($query->have_posts()) {
                $query->the_post();
                echo '<li><a href="' . esc_url(get_permalink()) . '">'
                    . esc_html(get_the_title()) . '</a></li>';
            }
            echo '</ul>';
        }

        wp_reset_postdata();
        echo $args['after_widget'];
    }

    public function form($instance) {
        $title = !empty($instance['title']) ? $instance['title'] : '';
        $count = !empty($instance['count']) ? absint($instance['count']) : 5;
        ?>
        <p>
            <label for="<?php echo esc_attr($this->get_field_id('title')); ?>">
                <?php esc_html_e('عنوان:', 'my-theme'); ?>
            </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('count')); ?>">
                <?php esc_html_e('تعداد:', 'my-theme'); ?>
            </label>
            <input class="tiny-text"
                   id="<?php echo esc_attr($this->get_field_id('count')); ?>"
                   name="<?php echo esc_attr($this->get_field_name('count')); ?>"
                   type="number"
                   value="<?php echo esc_attr($count); ?>"
                   min="1">
        </p>
        <?php
    }

    public function update($new_instance, $old_instance) {
        $instance = [];
        $instance['title'] = sanitize_text_field($new_instance['title']);
        $instance['count'] = absint($new_instance['count']);
        return $instance;
    }
}

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

نکته‌ی مهم در ساخت ویجت سفارشی، رعایت اصول امنیتی است. همیشه مقادیر فرم را با sanitize_text_field() یا absint() پاک‌سازی کنید. خروجی‌ها را با esc_html() و esc_url() فرار دهید. بدون این اقدامات، ویجت شما به یک دروازه‌ی XSS تبدیل می‌شود.

نکته‌ی ظریف دیگر، استفاده از wp_reset_postdata() بعد از حلقه‌ی WP_Query است. بدون این فراخوانی، متغیر سراسری $post تغییر می‌کند و ممکن است محتوای بعدی صفحه به‌درستی نمایش داده نشود.

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

ساخت ویجت بلاکی سفارشی

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

import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

registerBlockType('my-theme/recent-posts', {
    title: __('آخرین نوشته‌ها', 'my-theme'),
    icon: 'list-view',
    category: 'widgets',
    edit: ({ attributes, setAttributes }) => {
        const blockProps = useBlockProps();
        return (
            <div {...blockProps}>
                <p>{__('آخرین نوشته‌ها', 'my-theme')}</p>
            </div>
        );
    },
    save: () => null,
});

نکته‌ی مهم در ساخت ویجت بلاکی، استفاده از دسته‌ی widgets است. این دسته، به وردپرس می‌گوید که این بلاک در مناطق ویجت قابل استفاده است. همچنین، برای رندر داینامیک، از save: () => null استفاده می‌کنیم و رندر واقعی را به PHP واگذار می‌کنیم.

register_block_type('my-theme/recent-posts', [
    'render_callback' => function($attributes) {
        $query = new WP_Query([
            'posts_per_page' => 5,
            'no_found_rows' => true,
        ]);

        if (!$query->have_posts()) {
            return '';
        }

        $output = '<ul class="recent-posts-widget">';
        while ($query->have_posts()) {
            $query->the_post();
            $output .= '<li><a href="' . esc_url(get_permalink()) . '">'
                . esc_html(get_the_title()) . '</a></li>';
        }
        $output .= '</ul>';

        wp_reset_postdata();

        return $output;
    },
]);

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

مهاجرت از ویجت کلاسیک به بلاک

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

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

function migrate_classic_widgets_to_blocks() {
    $sidebars = wp_get_sidebars_widgets();

    foreach ($sidebars as $sidebar_id => $widgets) {
        if (!is_array($widgets)) {
            continue;
        }

        foreach ($widgets as $widget_id) {
            if (strpos($widget_id, 'my_widget-') !== 0) {
                continue;
            }

            $number = (int) str_replace('my_widget-', '', $widget_id);
            $options = get_option('widget_my_widget');

            if (!isset($options[$number])) {
                continue;
            }

            // تبدیل به بلاک
            $block_content = '<!-- wp:my-theme/my-widget -->'
                . serialize_block([
                    'blockName' => 'my-theme/my-widget',
                    'attrs' => $options[$number],
                    'innerBlocks' => [],
                    'innerHTML' => '',
                ]);

            // ذخیره در مناطق ویجت بلاکی
            // ...
        }
    }
}

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

شرایط نمایش و مدیریت پویا

یکی از نیازهای رایج در مدیریت ویجت‌ها، نمایش شرطی آن‌ها بر اساس شرایط مختلف است. مثلاً نمایش یک ویجت فقط در صفحه‌ی اصلی، یا فقط برای کاربران وارد‌شده. وردپرس به‌صورت پیش‌فرض این قابلیت را ندارد، اما می‌توانید با فیلترها آن را اضافه کنید.

add_filter('widget_display_callback', function($instance, $widget, $args) {
    if ($widget->id_base === 'my_widget') {
        if (!is_front_page()) {
            return false;
        }
    }
    return $instance;
}, 10, 3);

این فیلتر، قبل از رندر هر ویجت فراخوانی می‌شود و به شما اجازه می‌دهد تصمیم بگیرید که ویجت نمایش داده شود یا نه. اگر false برگردانید، ویجت نمایش داده نمی‌شود.

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

add_filter('widget_display_callback', function($instance, $widget, $args) {
    $visibility = $instance['visibility'] ?? 'all';

    switch ($visibility) {
        case 'logged_in':
            if (!is_user_logged_in()) {
                return false;
            }
            break;
        case 'logged_out':
            if (is_user_logged_in()) {
                return false;
            }
            break;
        case 'mobile':
            if (!wp_is_mobile()) {
                return false;
            }
            break;
    }

    return $instance;
}, 10, 3);

نکته‌ی مهم در مدیریت پویا، ذخیره‌سازی شرایط در تنظیمات ویجت است. در متد form() و update()، باید فیلدهای شرایط را اضافه کنید و مقادیر آن‌ها را ذخیره کنید.

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

مدیریت برنامه‌نویسی‌شده ویجت‌ها

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

function set_default_widgets() {
    $sidebars = get_option('sidebars_widgets', []);

    if (!empty($sidebars['sidebar-main'])) {
        return;
    }

    // تنظیمات ویجت جستجو
    $search_options = get_option('widget_search', []);
    $search_options[2] = ['title' => 'جستجو'];
    update_option('widget_search', $search_options);

    // تنظیمات ویجت آخرین نوشته‌ها
    $recent_options = get_option('widget_recent-posts', []);
    $recent_options[2] = ['title' => 'آخرین نوشته‌ها', 'number' => 5];
    update_option('widget_recent-posts', $recent_options);

    // قرار دادن ویجت‌ها در سایدبار
    $sidebars['sidebar-main'] = ['search-2', 'recent-posts-2'];
    update_option('sidebars_widgets', $sidebars);
}
add_action('after_switch_theme', 'set_default_widgets');

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

نکته‌ی ظریف دیگر، بررسی وجود ویجت‌های قبلی است. اگر سایدبار از قبل دارای ویجت باشد، نباید آن‌ها را بازنویسی کنید. در کد بالا، این بررسی با !empty($sidebars['sidebar-main']) انجام شده است.

برای درک عمیق‌تر نحوه‌ی کار با تنظیمات وردپرس، مقاله ساخت صفحه تنظیمات اختصاصی در وردپرس را مطالعه کنید.

کارایی و بهینه‌سازی ویجت‌ها

ویجت‌ها، اگر به‌درستی نوشته نشوند، می‌توانند به یک گلوگاه کارایی تبدیل شوند. هر ویجت، یک قطعه‌ی کد است که در هر بار بارگذاری صفحه اجرا می‌شود. اگر تعداد ویجت‌ها زیاد باشد یا هر ویجت کوئری‌های سنگینی اجرا کند، زمان بارگذاری صفحه به‌طور محسوسی افزایش می‌یابد.

نوع ویجتمنبع کندیراه‌حل
آخرین نوشته‌هاکوئری تکراری در هر صفحهکش با Transient API
محصولات پرفروشکوئری پیچیده روی ووکامرسکش و ایندکس‌گذاری
نظرات اخیرکوئری روی جدول بزرگمحدود کردن تعداد و کش
ویجت‌های شخص ثالثاسکریپت‌های خارجیبارگذاری تأخیری

سه تکنیک اصلی برای بهینه‌سازی ویجت‌ها وجود دارد. تکنیک اول، کش کردن خروجی. اگر ویجت شما خروجی سنگینی تولید می‌کند، می‌توانید آن را با Transient API کش کنید.

public function widget($args, $instance) {
    $cache_key = 'my_widget_' . md5(serialize($instance));
    $output = get_transient($cache_key);

    if (false === $output) {
        ob_start();
        // تولید خروجی
        $output = ob_get_clean();
        set_transient($cache_key, $output, HOUR_IN_SECONDS);
    }

    echo $output;
}

تکنیک دوم، بهینه‌سازی کوئری‌ها. اگر ویجت شما از WP_Query استفاده می‌کند، پارامترهای بهینه‌سازی مثل no_found_rows و update_post_meta_cache را تنظیم کنید.

$query = new WP_Query([
    'posts_per_page' => 5,
    'no_found_rows' => true,
    'update_post_meta_cache' => false,
    'update_post_term_cache' => false,
    'ignore_sticky_posts' => true,
]);

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

add_action('wp_enqueue_scripts', function() {
    if (!is_active_sidebar('sidebar-shop')) {
        return;
    }

    if (!is_shop() && !is_product()) {
        return;
    }

    wp_enqueue_style('my-widget-shop', get_template_directory_uri() . '/css/widget-shop.css');
});

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

امنیت در ویجت‌ها

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

اصل اول، پاک‌سازی ورودی‌ها. در متد update()، همه‌ی مقادیر ورودی را پاک‌سازی کنید.

public function update($new_instance, $old_instance) {
    $instance = [];
    $instance['title'] = sanitize_text_field($new_instance['title']);
    $instance['count'] = absint($new_instance['count']);
    $instance['url'] = esc_url_raw($new_instance['url']);
    $instance['content'] = wp_kses_post($new_instance['content']);
    return $instance;
}

اصل دوم، فرار دادن خروجی‌ها. در متد widget()، همه‌ی مقادیر را فرار دهید.

echo $args['before_title'] . esc_html($title) . $args['after_title'];
echo '<p>' . wp_kses_post($content) . '</p>';
echo '<a href="' . esc_url($url) . '">' . esc_html($link_text) . '</a>';

اصل سوم، بررسی سطح دسترسی. اگر ویجت شما عملیات حساسی انجام می‌دهد، در متد form() بررسی کنید که کاربر جاری مجوز لازم را دارد.

public function form($instance) {
    if (!current_user_can('edit_theme_options')) {
        return;
    }
    // فرم
}

اصل چهارم، جلوگیری از اجرای تکراری. در ویجت‌هایی که کوئری‌های سنگین اجرا می‌کنند، از یک متغیر استاتیک برای جلوگیری از اجرای تکراری استفاده کنید.

public function widget($args, $instance) {
    static $rendered = false;
    if ($rendered) {
        return;
    }
    $rendered = true;
    // ادامه
}

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

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

اشتباهات رایج در مدیریت ویجت‌ها

در بازبینی پروژه‌های مختلف، اشتباهات تکراری در مدیریت ویجت‌ها دیده می‌شود که هر کدام می‌تواند به بدهی فنی یا مشکلات امنیتی منجر شود.

اشتباه اول، عدم بررسی is_active_sidebar(). اگر قبل از فراخوانی dynamic_sidebar() بررسی نکنید که سایدبار فعال است، ممکن است خطا یا خروجی نامناسب دریافت کنید.

// نادرست
dynamic_sidebar('sidebar-main');

// درست
if (is_active_sidebar('sidebar-main')) {
    dynamic_sidebar('sidebar-main');
}

اشتباه دوم، عدم فرار دادن خروجی‌ها. اگر مقادیر ویجت را بدون esc_html() یا esc_attr() نمایش دهید، ویجت شما به یک دروازه‌ی XSS تبدیل می‌شود.

اشتباه سوم، عدم پاک‌سازی ورودی‌ها. در متد update()، اگر مقادیر را بدون پاک‌سازی ذخیره کنید، داده‌های آلوده در دیتابیس ذخیره می‌شوند.

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

اشتباه پنجم، عدم کش کردن خروجی. اگر ویجت شما کوئری سنگینی اجرا می‌کند و خروجی را کش نمی‌کنید، هر بار بارگذاری صفحه، کوئری تکراری اجرا می‌شود.

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

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

اشتباه هشتم، عدم توجه به دسترس‌پذیری. اگر ویجت شما فرم دارد و برچسب‌های مناسب ندارد، برای کاربران با ناتوانی حرکتی غیرقابل استفاده می‌شود.

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

نگاهی مهندسی به لایه‌های پیشرفته ویجت

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

مفهوم اول، جداسازی لایه‌ها. یک ویجت حرفه‌ای، حداقل از سه لایه تشکیل می‌شود: لایه‌ی ارائه (متد widget())، لایه‌ی منطق (کلاس سرویس جداگانه) و لایه‌ی دسترسی به داده (کلاس Repository). این جداسازی، تست‌پذیری و نگهداری را به‌طور قابل‌توجهی بهبود می‌دهد.

class My_Widget_Service {
    public function get_recent_posts($count) {
        return get_posts([
            'posts_per_page' => $count,
            'no_found_rows' => true,
        ]);
    }
}

class My_Widget extends WP_Widget {
    protected $service;

    public function __construct(My_Widget_Service $service) {
        $this->service = $service;
        parent::__construct('my_widget', __('ویجت من', 'my-theme'));
    }

    public function widget($args, $instance) {
        $posts = $this->service->get_recent_posts($instance['count']);
        // رندر
    }
}

مفهوم دوم، کش کردن لایه‌ای. به‌جای کش کردن کل خروجی HTML، می‌توانید داده‌های خام را کش کنید و سپس در هر درخواست، HTML را از داده‌های کش‌شده بسازید. این رویکرد، انعطاف‌پذیری بیشتری فراهم می‌کند.

مفهوم سوم، سازگاری دوگانه. ویجت‌های مدرن باید هم با ویرایشگر کلاسیک و هم با ویرایشگر بلاکی سازگار باشند. این یعنی باید هم کلاس PHP و هم بلاک JavaScript را ارائه دهید، یا حداقل از بلاک Legacy Widget پشتیبانی کنید.

مفهوم چهارم، مدیریت وضعیت. در ویجت‌های بلاکی، مدیریت وضعیت (State Management) با React انجام می‌شود. برای هماهنگی با سرور، از useSelect و useDispatch استفاده کنید.

import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

const posts = useSelect((select) => {
    return select(coreStore).getEntityRecords('postType', 'post', {
        per_page: 5,
    });
}, []);

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

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

پرسش‌های پرتکرار درباره مدیریت ویجت‌ها

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

چرا ویجت من در فرانت‌اند نمایش داده نمی‌شود؟ دلایل متعددی وجود دارد: ممکن است سایدبار ثبت نشده باشد، ممکن است dynamic_sidebar() در قالب فراخوانی نشده باشد، ممکن است ویجت در پنل مدیریت ثبت نشده باشد، یا ممکن است is_active_sidebar() بررسی نشده باشد.

چگونه یک سایدبار جدید ثبت کنم؟ از تابع register_sidebar() در هوک widgets_init استفاده کنید. پارامترهای name، id و before_widget را تعریف کنید.

چگونه یک ویجت سفارشی بسازم؟ یک کلاس تعریف کنید که از WP_Widget ارث‌بری کند. متدهای widget()، form() و update() را پیاده‌سازی کنید. سپس با register_widget() آن را ثبت کنید.

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

چگونه از ویجت‌های کلاسیک در ویرایشگر بلاکی استفاده کنم؟ وردپرس یک بلاک ویژه به نام «ویجت کلاسیک» (Legacy Widget) ارائه کرده است که امکان استفاده از ویجت‌های قدیمی در ویرایشگر بلاکی را فراهم می‌کند.

چگونه ویجت‌ها را به‌صورت برنامه‌نویسی‌شده مدیریت کنم؟ از توابع wp_get_sidebars_widgets() و wp_set_sidebars_widgets() برای خواندن و نوشتن وضعیت ویجت‌ها استفاده کنید. همچنین تنظیمات هر ویجت در get_option('widget_{id_base}') ذخیره می‌شود.

چگونه ویجت‌ها را در صفحات خاصی نمایش دهم؟ از فیلتر widget_display_callback استفاده کنید. در این فیلتر، می‌توانید بر اساس شرایط، مقدار false برگردانید تا ویجت نمایش داده نشود.

چگونه کارایی ویجت‌ها را بهبود دهم؟ خروجی را با Transient API کش کنید، کوئری‌ها را با پارامترهای بهینه‌سازی مثل no_found_rows بهینه کنید و اسکریپت‌ها را فقط در صفحات مرتبط بارگذاری کنید.

چگونه امنیت ویجت‌ها را تضمین کنم؟ در متد update() ورودی‌ها را پاک‌سازی کنید، در متد widget() خروجی‌ها را فرار دهید و در متد form() سطح دسترسی کاربر را بررسی کنید.

آیا می‌توانم چند سایدبار با یک کد ثبت کنم؟ بله، می‌توانید از یک حلقه استفاده کنید و چند سایدبار را با شناسه‌های متفاوت ثبت کنید. این رویکرد، کد را تمیزتر می‌کند.

چگونه از ویجت‌ها در قالب‌های FSE استفاده کنم؟ در قالب‌های Full Site Editing (FSE)، مناطق ویجت با بخش‌های قالب (Template Parts) جایگزین شده‌اند. برای استفاده از ویجت‌ها در این قالب‌ها، باید از بلاک‌های سازگار استفاده کنید.

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

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

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

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

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