متد wpdb::delete ابزار استاندارد کلاس wpdb برای حذف امن رکوردها در جداول وردپرس است. این متد با استفاده از prepared statement و پارامتر where، از حذف ناخواسته و SQL Injection جلوگیری می‌کند. تشخیص درست ساختار where و استفاده از شرط‌های دقیق، پایه پیاده‌سازی حرفه‌ای حذف داده محسوب می‌شود. اشتباهات رایجی مانند نبود where، نبود capability، نبود nonce و نبود تست می‌تواند به فاجعه‌های جبران‌ناپذیر مانند حذف کل داده‌های جدول منجر شود. تسلط بر این متد برای افزونه‌نویسی حرفه‌ای ضروری است و در مدیریت داده کاربرد جدی دارد.

چرا حذف امن حیاتی است؟

حذف داده در پایگاه داده، حساس‌ترین عملیات یک افزونه است. یک اشتباه کوچک می‌تواند به از دست رفتن داده‌های غیرقابل بازیابی منجر شود. اگر شرط where اشتباه باشد، ممکن است کل جدول پاک شود. اگر sanitize انجام نشود، ممکن است به SQL Injection منجر شود. در پروژه‌های واقعی، حوادث متعددی از حذف اشتباه داده گزارش شده است. یک افزونه که به‌جای حذف یک رکورد، همه رکوردها را حذف کرده است. یک دستور دستی که به‌جای یک شرط، تمام شرط‌ها را حذف کرده است. این حوادث نشان می‌دهد که استفاده درست از متد wpdb::delete یک ضرورت جدی است.

متد wpdb::delete چیست؟

متد wpdb::delete() یک متد از کلاس wpdb در وردپرس است که در فایل wp-includes/wp-db.php تعریف شده است. این متد یک یا چند رکورد را از جدول مشخص حذف می‌کند. مکانیزم امنیتی این متد بر پارامتر where استوار است. اگر where خالی باشد، متد خطا می‌دهد و هیچ کوئری ارسال نمی‌کند. این محافظت داخلی، از فاجعه‌بارترین حالت یعنی حذف کل جدول جلوگیری می‌کند. نکته مهم این است که این متد برای حذف سخت (Hard Delete) طراحی شده است. برای حذف نرم (Soft Delete)، باید از wpdb::update با ستون وضعیت استفاده کنید.

امضای متد و پارامترها

امضای این متد به‌شکل زیر است:
public function delete( $table, $where, $where_format = null ) {
    // ...
}
پارامتر اول (table) نام جدول است که باید شامل prefix وردپرس باشد. پارامتر دوم (where) آرایه انجمنی از شرایط است که رکوردهای هدف را مشخص می‌کند. این پارامتر اجباری است. پارامتر سوم (where_format) آرایه format برای ستون‌های where است. خروجی متد یک عدد صحیح است: تعداد رکوردهای حذف‌شده یا مقدار false در صورت خطا.

نقش حیاتی where

پارامتر where تنها محافظت در برابر حذف ناخواسته است. اگر این پارامتر اشتباه باشد، ممکن است رکوردهای ناخواسته حذف شوند. نمونه صحیح:
global $wpdb;

$wpdb->delete(
    $wpdb->prefix . 'myplugin_logs',
    array( 'id' => 42 ),
    array( '%d' )
);
این نمونه تنها رکورد با شناسه ۴۲ را حذف می‌کند. نمونه با شرط ترکیبی:
$wpdb->delete(
    $wpdb->prefix . 'myplugin_items',
    array(
        'user_id' => get_current_user_id(),
        'id'      => absint( $item_id ),
    ),
    array( '%d', '%d' )
);
نکته مهم: افزودن شرط user_id در where از حذف داده کاربران دیگر توسط کاربر جاری جلوگیری می‌کند. این رویکرد در پروژه‌های چندکاربری ضروری است.

مقدار بازگشتی

مقدار بازگشتی این متد می‌تواند سه حالت داشته باشد: - 0: هیچ رکوردی حذف نشد (where هیچ رکوردی پیدا نکرد) - عدد مثبت: تعداد رکوردهای حذف‌شده - false: خطا در کوئری نکته مهم: مقدار 0 به‌معنای خطا نیست. اگر where هیچ رکوردی پیدا نکند، مقدار 0 برگردانده می‌شود. الگوی صحیح بررسی:
$result = $wpdb->delete( $table, $where, $where_format );

if ( false === $result ) {
    error_log( 'Delete failed: ' . $wpdb->last_error );
    return false;
}

if ( 0 === $result ) {
    return 'not_found';
}

return 'deleted';

حذف نرم به‌جای حذف دائمی

در بسیاری از سناریوها، حذف دائمی داده توصیه نمی‌شود. اگر کاربری به‌اشتباه دکمه حذف را بزند، داده برای همیشه از بین می‌رود. راه‌حل، استفاده از Soft Delete است. الگوی Soft Delete با ستون وضعیت:
function myplugin_soft_delete_item( $item_id, $user_id ) {
    global $wpdb;

    return $wpdb->update(
        $wpdb->prefix . 'myplugin_items',
        array(
            'status'     => 'deleted',
            'deleted_at' => current_time( 'mysql' ),
        ),
        array(
            'id'      => absint( $item_id ),
            'user_id' => absint( $user_id ),
        ),
        array( '%s', '%s' ),
        array( '%d', '%d' )
    );
}
با این الگو، داده در پایگاه باقی می‌ماند اما از نمایش خارج می‌شود. امکان بازیابی در صورت نیاز وجود دارد. راهنمای متد update در صفحه wpdb::update آمده است. حذف دائمی در زمان مدیریت (Admin Cleanup):
function myplugin_purge_soft_deleted() {
    global $wpdb;

    $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) );

    return $wpdb->query( $wpdb->prepare(
        "DELETE FROM {$wpdb->prefix}myplugin_items
         WHERE status = %s AND deleted_at < %s",
        'deleted',
        $cutoff
    ) );
}

کاربردهای عملی در افزونه

حذف لاگ قدیمی:
function myplugin_delete_old_logs( $days = 30 ) {
    global $wpdb;

    $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-' . absint( $days ) . ' days' ) );

    return $wpdb->query( $wpdb->prepare(
        "DELETE FROM {$wpdb->prefix}myplugin_events
         WHERE created_at < %s",
        $cutoff
    ) );
}
حذف رکورد با بررسی مالکیت:
function myplugin_delete_user_item( $item_id ) {
    global $wpdb;

    $user_id = get_current_user_id();

    if ( ! $user_id ) {
        return false;
    }

    $item = $wpdb->get_row( $wpdb->prepare(
        "SELECT id, user_id FROM {$wpdb->prefix}myplugin_items WHERE id = %d",
        absint( $item_id )
    ) );

    if ( ! $item ) {
        return 'not_found';
    }

    if ( (int) $item->user_id !== $user_id && ! current_user_can( 'manage_options' ) ) {
        return 'forbidden';
    }

    return $wpdb->delete(
        $wpdb->prefix . 'myplugin_items',
        array( 'id' => absint( $item_id ) ),
        array( '%d' )
    );
}
حذف از طریق AJAX با بررسی کامل امنیتی:
add_action( 'wp_ajax_myplugin_delete_item', 'myplugin_delete_item_handler' );
function myplugin_delete_item_handler() {
    check_ajax_referer( 'myplugin_delete_nonce', 'nonce' );

    if ( ! is_user_logged_in() ) {
        wp_send_json_error( array( 'message' => 'ابتدا وارد شوید' ), 401 );
    }

    $item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;

    if ( ! $item_id ) {
        wp_send_json_error( array( 'message' => 'شناسه نامعتبر' ), 400 );
    }

    $result = myplugin_delete_user_item( $item_id );

    if ( 'not_found' === $result ) {
        wp_send_json_error( array( 'message' => 'رکورد پیدا نشد' ), 404 );
    }

    if ( 'forbidden' === $result ) {
        wp_send_json_error( array( 'message' => 'دسترسی غیرمجاز' ), 403 );
    }

    if ( false === $result ) {
        wp_send_json_error( array( 'message' => 'خطا در حذف' ), 500 );
    }

    wp_send_json_success( array( 'message' => 'با موفقیت حذف شد' ) );
}
راهنمای توابع استفاده‌شده در این بخش: check_ajax_referer، is_user_logged_in، current_user_can، wp_send_json_success و wp_send_json_error.

پاک‌سازی دوره‌ای داده

پاک‌سازی دوره‌ای داده قدیمی، یکی از اصول نگهداری پایگاه داده است. وردپرس امکان زمان‌بندی این کار را با Cron فراهم می‌کند:
add_action( 'myplugin_daily_cleanup', 'myplugin_run_daily_cleanup' );

function myplugin_schedule_cleanup() {
    if ( ! wp_next_scheduled( 'myplugin_daily_cleanup' ) ) {
        wp_schedule_event( time(), 'daily', 'myplugin_daily_cleanup' );
    }
}
register_activation_hook( __FILE__, 'myplugin_schedule_cleanup' );

function myplugin_run_daily_cleanup() {
    global $wpdb;

    // حذف لاگ‌های قدیمی‌تر از ۳۰ روز
    $cutoff = gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) );

    $deleted = $wpdb->query( $wpdb->prepare(
        "DELETE FROM {$wpdb->prefix}myplugin_events
         WHERE created_at < %s",
        $cutoff
    ) );

    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        error_log( sprintf(
            '[myplugin] Daily cleanup: %d rows deleted',
            (int) $deleted
        ) );
    }
}
نکته مهم: در پاک‌سازی دوره‌ای، همیشه از یک بازه زمانی محافظه‌کارانه استفاده کنید تا امکان بازیابی وجود داشته باشد. راهنمای هوک فعال‌سازی در صفحه register_activation_hook آمده است.

نکات امنیتی و اشتباهات رایج

اشتباه اول، نبود where است. اگر where خالی باشد، متد خطا می‌دهد. اما اگر ساختار نادرست باشد، ممکن است همه رکوردها حذف شوند. اشتباه دوم، نبود current_user_can است. اگر حذف از فرم یا AJAX انجام شود، بررسی دسترسی الزامی است. اشتباه سوم، نبود nonce است. برای جلوگیری از CSRF، بررسی nonce ضروری است. راهنمای این تابع در صفحه wp_verify_nonce آمده است. اشتباه چهارم، نبود بررسی مالکیت است. اگر کاربری بتواند رکورد کاربران دیگر را حذف کند، حفره امنیتی جدی ایجاد می‌شود. اشتباه پنجم، نبود لاگ‌گیری است. حذف داده باید در لاگ سرور ثبت شود تا در صورت بروز مشکل، قابل ردیابی باشد. اشتباه ششم، حذف انبوه بدون محدودیت زمانی است. اگر بازه زمانی بسیار وسیع باشد، ممکن است داده‌های مهم حذف شوند. اشتباه هفتم، نبود بکاپ است. پیش از هر عملیات حذف انبوه، باید بکاپ گرفته شود. اشتباه هشتم، نبود تست است. باید سناریوهای حذف موفق، عدم وجود رکورد، خطا و فقدان دسترسی را بررسی کنید.

تحلیل فنی پیشرفته

در نگاه مهندسی، متد wpdb::delete() یک نقطه معماری در لایه Data Deletion است که بر چند جنبه از سیستم اثر می‌گذارد. لایه اول لایه Prepared Statement است. این متد به‌صورت خودکار پارامترها را escape و کوئری را آماده می‌کند. لایه دوم لایه Where Validation است. اگر where خالی باشد، متد خطا می‌دهد. این محافظت داخلی از فاجعه‌بارترین حالت جلوگیری می‌کند. لایه سوم لایه Soft vs Hard Delete است. برای داده‌های مهم، استفاده از Soft Delete توصیه می‌شود. این رویکرد امکان بازیابی را فراهم می‌کند. لایه چهارم لایه Referential Integrity است. اگر جدول شما Foreign Key دارد، حذف رکورد ممکن است به خطای Constraint منجر شود. باید ترتیب حذف را رعایت کنید. لایه پنجم لایه Performance است. حذف تعداد زیادی رکورد در یک عملیات می‌تواند جدول را قفل کند. برای حذف انبوه، بهتر است در دسته‌های کوچک انجام شود. لایه ششم لایه Security است. حذف باید با بررسی capability، nonce و مالکیت همراه باشد. لایه هفتم لایه Auditability است. حذف داده مهم باید در لاگ ثبت شود. لایه هشتم لایه Multisite است. در شبکه‌های Multisite، هر سایت جدول مستقل دارد. لایه نهم لایه Testing است. تست‌های واحد باید همه سناریوها را پوشش دهند. مفاهیم پایه‌ای Referential Integrity در Referential integrity در ویکی‌پدیا توضیح داده شده است. برای مطالعه بیشتر روی متدهای مرتبط، می‌توانید به راهنمای wpdb::insert، راهنمای wpdb::update، راهنمای wpdb::prepare، راهنمای wpdb::get_results، راهنمای current_user_can، راهنمای wp_verify_nonce و راهنمای register_activation_hook مراجعه کنید.

پرسش‌های پرتکرار

اگر where خالی باشد، چه اتفاقی می‌افتد؟ متد خطا می‌دهد و کوئری ارسال نمی‌شود. تفاوت مقدار 0 و false در مقدار بازگشتی چیست؟ 0 به‌معنای عدم یافتن رکورد و false به‌معنای خطا است. آیا می‌توان چند شرط در where داشت؟ بله، به‌صورت آرایه با چند کلید. آیا حذف قابل بازگشت است؟ در حذف سخت، خیر. برای امکان بازیابی، از Soft Delete استفاده کنید. آیا می‌توان همه رکوردها را حذف کرد؟ متد delete برای این کار طراحی نشده است. باید از کوئری مستقیم با احتیاط استفاده کنید.

ادامه مسیر

متد wpdb::delete() ابزار استاندارد وردپرس برای حذف امن داده است. استفاده درست از آن یعنی تعریف دقیق where، بررسی capability و nonce، توجه به مالکیت، لاگ‌گیری عملیات مهم و در نظر گرفتن Soft Delete برای داده‌های حساس. اشتباه‌های کوچک در این متد اغلب به فاجعه‌های جبران‌ناپذیر منجر می‌شوند. اگر این متد را در پروژه‌ای واقعی به کار برده‌اید و رفتار غیرمنتظره‌ای دیده‌اید — به‌خصوص در حذف انبوه یا در Multisite — تجربه‌تان می‌تواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.