تابع add_settings_field() در وردپرس مسئول تعریف یک فیلد ورودی در پنل تنظیمات است و به‌عنوان سومین جزء از سه‌گانه Settings API، رابط کاربری دریافت مقادیر تنظیمات را شکل می‌دهد. بدون این تابع، کاربر قادر به وارد کردن داده در پنل نخواهد بود حتی اگر تنظیمات و بخش‌ها به‌درستی ثبت شده باشند.

تابع add_settings_field وردپرس یکی از پرکاربردترین توابع Settings API برای ساخت فیلدهای تنظیمات افزونه‌هاست. این تابع امکان تعریف label، callback، نوع ورودی و اتصال به بخش را فراهم می‌کند و پایه تجربه کاربری پنل مدیریت محسوب می‌شود. در این راهنما ساختار کامل، پارامترها، نمونه‌های واقعی، انواع فیلدها، اشتباهات رایج و نکات امنیتی این تابع بررسی می‌شود. همچنین تفاوت آن با add_settings_section و register_setting توضیح داده می‌شود. در پایان پرسش‌های پرتکرار و نگاه فنی عمیق به این تابع مرور خواهد شد.

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

چرا add_settings_field اهمیت دارد

وردپرس برای فرم‌های تنظیمات افزونه‌ها یک ساختار استاندارد ارائه می‌دهد. هر فرم از چند بخش تشکیل می‌شود که هر بخش شامل چند فیلد است. سه تابع اصلی این ساختار را می‌سازند:

  • register_setting برای ثبت تنظیم
  • add_settings_section برای تعریف بخش
  • add_settings_field برای تعریف فیلد

تابع add_settings_field() به‌طور مشخص مسئول تعریف یک فیلد ورودی است. این تابع سه چیز را کنترل می‌کند: عنوانی که در ستون سمت چپ نمایش داده می‌شود، callback که عنصر HTML ورودی را تولید می‌کند، و بخشی که فیلد در آن قرار می‌گیرد.

برای درک کامل جایگاه این تابع در کنار سایر اجزا، مطلب تابع register_setting و تابع add_settings_section را مطالعه کنید.

ساختار و امضای تابع add_settings_field

امضای این تابع به شکل زیر است:

add_settings_field( string $id, string $title, callable $callback, string $page, string $section = 'default', array $args = array() ): void

پارامتر اول شناسه یکتای فیلد است که در HTML به‌عنوان id استفاده می‌شود. پارامتر دوم عنوانی است که در ستون سمت چپ فرم نمایش داده می‌شود. پارامتر سوم callback تولید عنصر ورودی است. پارامتر چهارم صفحه‌ای است که فیلد در آن نمایش داده می‌شود. پارامتر پنجم شناسه بخش است. پارامتر ششم آرایه‌ای از آرگومان‌های اضافی است که به callback پاس داده می‌شود.

خروجی این تابع void است و رکورد را در آرایه سراسری $wp_settings_fields ثبت می‌کند.

پارامترهای کلیدی و کاربرد هرکدام

پارامتر id

شناسه یکتای فیلد. این شناسه در HTML به‌عنوان id روی عنصر استفاده می‌شود و برای اتصال label به input ضروری است:

add_settings_field(
    'myplugin_api_key_field',
    'کلید API',
    'myplugin_api_key_render',
    'myplugin_settings',
    'myplugin_main_section'
);

پارامتر title

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

add_settings_field(
    'myplugin_mode_field',
    'حالت فعال‌سازی',
    'myplugin_mode_render',
    'myplugin_settings',
    'myplugin_main_section'
);

پارامتر callback

تابعی که عنصر ورودی HTML را تولید می‌کند. این callback مسئول سه چیز است:

  • خواندن مقدار فعلی از دیتابیس
  • escape کردن مقدار برای attribute
  • خروجی HTML معتبر
function myplugin_api_key_render() {
    $value = get_option( 'myplugin_api_key' );
    printf(
        '<input type="text" id="myplugin_api_key_field" name="myplugin_api_key" value="%s" class="regular-text" />',
        esc_attr( $value )
    );
}

پارامتر page

نام صفحه‌ای که فیلد در آن نمایش داده می‌شود. این مقدار باید با مقدار صفحه در add_settings_section و do_settings_sections یکسان باشد:

do_settings_sections( 'myplugin_settings' );

برای مطالعه ساخت صفحه، مطلب تابع add_options_page را ببینید.

پارامتر section

شناسه بخشی که فیلد در آن قرار می‌گیرد. این مقدار باید با مقدار id در add_settings_section یکسان باشد:

add_settings_field(
    'myplugin_api_key_field',
    'کلید API',
    'myplugin_api_key_render',
    'myplugin_settings',
    'myplugin_main_section'  // این نام باید با id section یکسان باشد
);

اگر این مقدار اشتباه باشد، فیلد در صفحه نمایش داده نمی‌شود.

پارامتر args

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

add_settings_field(
    'myplugin_color_field',
    'رنگ اصلی',
    'myplugin_field_render',
    'myplugin_settings',
    'myplugin_main_section',
    array(
        'label_for' => 'myplugin_color_field',
        'class'     => 'myplugin-color-row',
    )
);

function myplugin_field_render( $args ) {
    printf(
        '<input type="color" id="%s" name="myplugin_color" value="%s" />',
        esc_attr( $args['label_for'] ),
        esc_attr( get_option( 'myplugin_color' ) )
    );
}

انواع فیلد و نحوه پیاده‌سازی هرکدام

فیلد متنی ساده

function myplugin_text_render() {
    printf(
        '<input type="text" name="myplugin_text" value="%s" class="regular-text" />',
        esc_attr( get_option( 'myplugin_text' ) )
    );
}

فیلد Email

function myplugin_email_render() {
    printf(
        '<input type="email" name="myplugin_email" value="%s" class="regular-text" />',
        esc_attr( get_option( 'myplugin_email' ) )
    );
}

برای sanitize ایمیل، مطلب راهنمای Sanitization را ببینید.

فیلد Checkbox

function myplugin_checkbox_render() {
    $checked = get_option( 'myplugin_enabled' ) ? 'checked' : '';
    printf(
        '<label><input type="checkbox" name="myplugin_enabled" value="1" %s /> %s</label>',
        $checked,
        esc_html__( 'فعال‌سازی افزونه', 'my-plugin' )
    );
}

فیلد Select

function myplugin_select_render() {
    $current = get_option( 'myplugin_mode', 'auto' );
    $options = array(
        'auto'   => 'خودکار',
        'manual' => 'دستی',
    );
    echo '<select name="myplugin_mode">';
    foreach ( $options as $key => $label ) {
        printf(
            '<option value="%s" %s>%s</option>',
            esc_attr( $key ),
            selected( $current, $key, false ),
            esc_html( $label )
        );
    }
    echo '</select>';
}

فیلد Textarea

function myplugin_textarea_render() {
    printf(
        '<textarea name="myplugin_notes" rows="5" cols="50">%s</textarea>',
        esc_textarea( get_option( 'myplugin_notes' ) )
    );
}

استفاده از esc_textarea برای متن داخل textarea ضروری است.

فیلد Radio

function myplugin_radio_render() {
    $current = get_option( 'myplugin_theme', 'light' );
    $options = array(
        'light' => 'روشن',
        'dark'  => 'تیره',
    );
    foreach ( $options as $key => $label ) {
        printf(
            '<label><input type="radio" name="myplugin_theme" value="%s" %s /> %s</label><br />',
            esc_attr( $key ),
            checked( $current, $key, false ),
            esc_html( $label )
        );
    }
}

فیلد Hidden برای داده‌های ثابت

function myplugin_hidden_render() {
    printf(
        '<input type="hidden" name="myplugin_version" value="%s" />',
        esc_attr( get_option( 'myplugin_version' ) )
    );
}

فیلد Password برای کلیدهای حساس

function myplugin_password_render() {
    printf(
        '<input type="password" name="myplugin_secret" value="%s" class="regular-text" autocomplete="new-password" />',
        esc_attr( get_option( 'myplugin_secret' ) )
    );
}

نمونه‌های عملی در پروژه واقعی

پنل تنظیمات کامل با چند نوع فیلد

add_action( 'admin_init', function () {
    register_setting( 'myplugin_settings', 'myplugin_api_key', array(
        'sanitize_callback' => 'sanitize_text_field',
    ) );
    register_setting( 'myplugin_settings', 'myplugin_mode', array(
        'sanitize_callback' => 'sanitize_key',
    ) );
    register_setting( 'myplugin_settings', 'myplugin_enabled', array(
        'type'              => 'boolean',
        'sanitize_callback' => function ( $v ) { return (bool) $v; },
    ) );

    add_settings_section( 'myplugin_main', 'تنظیمات اصلی', '__return_null', 'myplugin_settings' );

    add_settings_field( 'myplugin_api_key', 'کلید API', 'myplugin_api_key_render', 'myplugin_settings', 'myplugin_main' );
    add_settings_field( 'myplugin_mode', 'حالت اجرا', 'myplugin_mode_render', 'myplugin_settings', 'myplugin_main' );
    add_settings_field( 'myplugin_enabled', 'وضعیت', 'myplugin_enabled_render', 'myplugin_settings', 'myplugin_main' );
} );

نمایش فیلد بر اساس قابلیت کاربر

if ( current_user_can( 'manage_options' ) ) {
    add_settings_field(
        'myplugin_admin_key',
        'کلید مدیریت',
        'myplugin_admin_key_render',
        'myplugin_settings',
        'myplugin_admin_section'
    );
}

برای مطالعه کامل نقش‌ها، مطلب Capability و نقش‌های کاربری سفارشی را ببینید.

ترکیب با تنظیمات Custom Post Type

اگر فیلد تنظیمات به یک post type خاص مرتبط است، مطلب تابع register_post_type راهنماست.

ذخیره و بازیابی مقادیر

پس از ساخت فیلد، مقادیر با تابع get_option و تابع update_option مدیریت می‌شوند. برای پاک‌سازی، از تابع delete_option استفاده کنید.

ساخت فیلد پیچیده با Repeater

function myplugin_repeater_render() {
    $items = get_option( 'myplugin_items', array() );
    if ( ! is_array( $items ) ) {
        $items = array();
    }
    foreach ( $items as $i => $item ) {
        printf(
            '<p><input type="text" name="myplugin_items[%d]" value="%s" /></p>',
            absint( $i ),
            esc_attr( $item )
        );
    }
}

برای sanitize آرایه، از callback سفارشی در register_setting استفاده کنید. مطلب Input Validation در وردپرس راهنماست.

نمایش فیلد در صفحه سفارشی

اگر پنل تنظیمات در صفحه اختصاصی است، مطلب تابع add_menu_page و تابع add_options_page را ببینید.

حذف تنظیمات در زمان غیرفعال‌سازی

برای حذف مقادیر در زمان غیرفعال‌سازی افزونه، از تابع register_deactivation_hook و تابع delete_option استفاده کنید.

اشتباهات رایج در استفاده از add_settings_field

نبود label برای فیلد

پارامتر title در ستون سمت چپ نمایش داده می‌شود اما برای دسترس‌پذیری، بهتر است از label_for در args استفاده کنید تا برچسب به input متصل شود:

add_settings_field(
    'myplugin_field',
    'عنوان فیلد',
    'myplugin_render',
    'myplugin_settings',
    'myplugin_main',
    array( 'label_for' => 'myplugin_field' )
);

نبود sanitize_callback در register_setting

فیلد بدون sanitize معتبر، در واقع یک درِ باز برای ورود داده مخرب است. مطمئن شوید register_setting مربوطه sanitize_callback مناسب دارد. مطلب راهنمای Sanitization مرجع است.

نبود escape در callback

هر مقدار چاپ‌شده در HTML باید escape شود. عدم escape به XSS منجر می‌شود:

// اشتباه
printf( '<input value="%s" />', $value );

// درست
printf( '<input value="%s" />', esc_attr( $value ) );

مطلب Output Escaping در وردپرس راهنمای کامل است.

فراموشی nonce در فرم

اگر از settings_fields() استفاده کنید، nonce به‌طور خودکار اضافه می‌شود. اما اگر فرم را دستی ساخته‌اید، باید خودتان nonce اضافه کنید. مطلب Nonce در وردپرس را ببینید.

نبود capability check در نمایش فرم

فرم تنظیمات باید فقط برای کاربران دارای capability نمایش داده شود. بدون این بررسی، هر کاربر می‌تواند به تنظیمات دسترسی پیدا کند.

نبود بررسی صفحه و بخش

اگر پارامتر page یا section اشتباه باشد، فیلد در صفحه نمایش داده نمی‌شود. همیشه این مقادیر را با سایر توابع هماهنگ کنید.

نبود تست روی سناریوهای مرزی

تست‌هایی مثل «فیلد خالی»، «فیلد با مقدار طولانی»، «فیلد با کاراکتر یونیکد» و «فیلد با کد HTML» را حتماً بنویسید.

امنیت و عملکرد در add_settings_field

این تابع به‌تنهایی امنیت را تأمین نمی‌کند و در ترکیب با سه لایه زیر معنا پیدا می‌کند:

  • sanitize_callback در register_setting
  • escape در callback رندر فیلد
  • capability check در نمایش فرم

برای مطالعه جامع امنیت در Settings API، مطلب SQL Injection Prevention در وردپرس و Input Validation مرجع هستند.

از نظر عملکرد، add_settings_field هزینه قابل توجهی ندارد، اما callback رندر فیلد می‌تواند منبع هزینه باشد اگر کوئری سنگین اجرا کند. توصیه می‌شود:

  1. callback را سبک نگه دارید
  2. از خواندن مستقیم دیتابیس در callback پرهیز کنید
  3. مقدار را یک بار در ابتدای callback بخوانید و در متغیر ذخیره کنید

برای مطالعه الگوهای بهینه‌سازی پنل مدیریت، مطلب بهینه‌سازی سرعت پنل مدیریت توصیه می‌شود.

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

تفاوت add_settings_field با add_settings_section چیست؟

add_settings_section() یک بخش یا گروه تعریف می‌کند که می‌تواند شامل چند فیلد باشد، در حالی که add_settings_field() یک فیلد مشخص را تعریف می‌کند که در یک بخش قرار می‌گیرد.

آیا می‌توان فیلد را بدون بخش تعریف کرد؟

بله، اگر پارامتر section را تعریف نکنید، فیلد در بخش default قرار می‌گیرد. اما توصیه می‌شود همیشه یک بخش مشخص تعریف کنید.

چرا فیلد من در صفحه نمایش داده نمی‌شود؟

معمولاً سه دلیل: پارامتر page اشتباه، پارامتر section اشتباه، یا تابع do_settings_sections روی صفحه درست فراخوانی نشده است.

آیا می‌توان چند فیلد را به یک تنظیم متصل کرد؟

بله، چند فیلد می‌توانند به یک option_name متصل شوند اما منطق sanitize باید آرایه‌ای باشد. برای جلوگیری از پیچیدگی، توصیه می‌شود هر فیلد یک تنظیم جداگانه داشته باشد.

آیا می‌توان فیلد را فقط برای کاربران خاص نمایش داد؟

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

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

بله، با enqueue کردن اسکریپت‌های لازم در hook admin_enqueue_scripts و ساخت فیلدی که با دکمه به media uploader متصل می‌شود. مطلب هوک admin_enqueue_scripts راهنماست.

آیا add_settings_field با Settings API در وردپرس 6.x سازگار است؟

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

نگاه فنی عمیق به add_settings_field

در سطح معماری، add_settings_field() یک رکورد در آرایه سراسری $wp_settings_fields ثبت می‌کند. کلید این آرایه ترکیبی از نام صفحه و id بخش است. در زمان رندر با do_settings_sections()، وردپرس آرایه را می‌پیماید و برای هر فیلد، عنوان و callback را فراخوانی می‌کند.

نکته ظریف اول، مسئله ترتیب فراخوانی admin_init است. تمام توابع سه‌گانه Settings API باید در این hook اجرا شوند. اگر در hook دیرتر یا زودتر اجرا شوند، رفتار غیرمنتظره رخ می‌دهد.

نکته دوم، تعامل با فرم‌های AJAX است. اگر فیلدها را با AJAX ذخیره می‌کنید، باید API استاندارد Settings API را دور بزنید و از update_option مستقیم استفاده کنید. در این حالت، nonce و capability باید دستی بررسی شوند. مطلب هوک admin_enqueue_scripts الگوی این کار را نشان می‌دهد.

مسئله سوم، رفتار escape در callbackهای مختلف است. برای attribute از esc_attr، برای متن داخل HTML از esc_html، برای URL از esc_url و برای textarea از esc_textarea استفاده کنید. انتخاب اشتباه تابع escape، امنیت را به‌طور کامل تأمین نمی‌کند.

در نهایت، در پروژه‌های Enterprise توصیه می‌شود یک لایه Abstract بسازید که ساخت فیلدها را با تعریف declarative ممکن کند. به جای فراخوانی مکرر add_settings_field و register_setting، یک آرایه از تعاریف بدهید و بقیه کار را به یک Engine بسپارید. این کار از تکرار جلوگیری می‌کند و تست‌پذیری را بالا می‌برد. برای مطالعه بیشتر، مباحث توابع وردپرس برای گزینه‌های سایت و تابع register_meta مفید هستند. برای مطالعه بیشتر درباره وردپرس، WordPress در ویکی‌پدیا نقطه شروع خوبی است.

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