تابع wp_delete_post() در وردپرس عملیات حذف یک نوشته را با تمام پردازش‌های جانبی — پاکسازی متادیتا، حذف روابط taxonomy، پاک کردن commentها و اجرای hookهای مرتبط — انجام می‌دهد. هر پروژه‌ای که به پاک‌سازی محتوا یا مدیریت چرخه عمر داده نیاز دارد، در نهایت به این تابع می‌رسد.

تابع wp_delete_post یکی از پرکاربردترین توابع وردپرس برای حذف نوشته‌هاست. این تابع امکان حذف نرم (انتقال به trash) یا حذف قطعی را فراهم می‌کند و پایه عملیات پاک‌سازی محتوا محسوب می‌شود. در این راهنما ساختار کامل، پارامترها، نمونه‌های واقعی، هوک‌های مرتبط، اشتباهات رایج و نکات امنیتی و عملکردی این تابع بررسی می‌شود. همچنین تفاوت آن با wp_trash_post و wp_update_post توضیح داده می‌شود. در پایان پرسش‌های پرتکرار و نگاه مهندسی سطح بالای این تابع مرور خواهد شد.

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

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

وردپرس ذاتاً بر پایه یک دیتابیس رابطه‌ای طراحی شده که در آن هر پست با متادیتا، روابط دسته‌بندی، commentها و ریویژن‌ها در جدول‌های مختلف در ارتباط است. حذف مستقیم یک ردیف از جدول wp_posts بدون پاکسازی این روابط، به داده‌های یتیم (orphan data) و پوسیدگی دیتابیس منجر می‌شود.

تابع wp_delete_post() دقیقاً برای همین منظور ساخته شده است: یک عملیات حذف منطقی که همه‌ی وابستگی‌ها را در نظر می‌گیرد. این تابع ابتدا بررسی می‌کند که پست وجود دارد، سپس روابط مرتبط را پاک می‌کند و در نهایت hookهای لازم را اجرا می‌کند.

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

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

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

wp_delete_post( int $postid, bool $force_delete = false ): WP_Post|array|false|null

پارامتر اول، شناسه پستی است که باید حذف شود. پارامتر دوم مشخص می‌کند که آیا حذف نرم (انتقال به trash) انجام شود یا حذف قطعی. مقدار پیش‌فرض false است، یعنی پست به trash منتقل می‌شود.

خروجی تابع در حالت موفق، شیء WP_Post پست حذف‌شده است. اگر پست وجود نداشته باشد، مقدار null یا false برگردانده می‌شود که باید بررسی شود.

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

این تابع تنها دو پارامتر دارد، اما هرکدام اثری جدی روی رفتار نهایی دارند:

پارامتر postid

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

$deleted = wp_delete_post( 123, true );
if ( ! $deleted ) {
    error_log( 'حذف پست 123 شکست خورد' );
}

پارامتر force_delete

مهم‌ترین پارامتر این تابع. اگر true باشد، پست مستقیماً و بدون رفتن به trash حذف می‌شود. این تصمیم باید با دقت گرفته شود چون بازگشت‌پذیری ندارد:

wp_delete_post( $post_id, true );  // حذف قطعی
wp_delete_post( $post_id, false ); // انتقال به زباله‌دان

توجه داشته باشید که در برخی نصب‌های وردپرس، اگر constant EMPTY_TRASH_DAYS صفر باشد، trash غیرفعال می‌شود و حتی فراخوانی با false نیز به حذف قطعی منجر می‌شود. برای بررسی این موضوع مطلب توابع وردپرس برای داده پست را ببینید.

هوک‌های مرتبط با حذف پست

در طول فرآیند حذف، چندین hook اجرا می‌شود که هرکدام فرصت خاصی برای توسعه‌دهنده فراهم می‌کند:

  • before_delete_post: بلافاصله قبل از حذف قطعی
  • deleted_post: بعد از حذف موفق
  • delete_post: در فرآیند حذف (شامل trash)
  • wp_trash_post: هنگام انتقال به زباله‌دان
  • trashed_post: بعد از انتقال به زباله‌دان
  • untrashed_post: هنگام بازیابی از زباله‌دان

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

add_action( 'deleted_post', function ( $post_id, $post ) {
    global $wpdb;
    $wpdb->delete(
        $wpdb->prefix . 'custom_stats',
        array( 'post_id' => $post_id ),
        array( '%d' )
    );
}, 10, 2 );

در این نمونه، استفاده از متد wpdb::delete به جای کوئری مستقیم توصیه می‌شود چون به‌طور داخلی از prepared statement استفاده می‌کند.

همچنین اگر درون hook حذف دوباره wp_delete_post فراخوانی کنید، ممکن است حلقه ایجاد شود. راه‌حل استاندارد، جدا کردن منطق با flag یا استفاده از تابع remove_action است.

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

در ادامه چند الگو که در پروژه‌های واقعی پرکاربرد هستند را مرور می‌کنیم:

حذف پست با بررسی دسترسی

if ( ! current_user_can( 'delete_post', $post_id ) ) {
    wp_die( esc_html__( 'دسترسی غیرمجاز', 'my-plugin' ) );
}
$deleted = wp_delete_post( $post_id, true );
if ( $deleted ) {
    // لاگ موفقیت
}

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

حذف انبوه پست‌های قدیمی

$old_posts = get_posts( array(
    'post_type'   => 'post',
    'date_query'  => array( array( 'before' => '2 years ago' ) ),
    'numberposts' => 100,
    'fields'      => 'ids',
) );
foreach ( $old_posts as $id ) {
    wp_delete_post( $id, true );
}

در سایت‌های بزرگ، این عملیات باید با محدودیت تعداد و از طریق WP-CLI یا cron انجام شود. جزئیات اجرای این نوع اسکریپت‌ها در مطلب راهنمای WP-CLI پوشش داده شده است.

حذف یک پست با حفظ متادیتا

گاهی نیاز دارید قبل از حذف، مقادیر متادیتا را ذخیره کنید. مطلب تابع get_post_meta راهنمای این کار است. برای پاکسازی مستقل متادیتا نیز از تابع delete_post_meta استفاده کنید.

حذف پست سفارشی پس از تکمیل چرخه

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

حذف قطعی پست بدون اجرای hook

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

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

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

نبود capability check

بدون بررسی current_user_can( 'delete_post', $post_id )، هر کاربر می‌تواند محتوای هر پستی را حذف کند. این مهم‌ترین اشتباه است.

نبود nonce در فرم‌های حذف

هر لینک یا فرمی که در آن حذف انجام می‌شود، باید nonce داشته باشد. مطلب Nonce در وردپرس توضیحات کامل را دارد.

حذف قطعی بدون تأیید کاربر

ارسال force_delete = true در پاسخ به یک کلیک ساده، ریسک بالایی دارد. بهتر است ابتدا پست به trash منتقل شود و کاربر در مرحله دوم تأیید قطعی را انجام دهد.

نبود بررسی خروجی

اگر پست وجود نداشته باشد، تابع false برمی‌گرداند. اگر این خروجی بررسی نشود، ممکن است پیام موفقیت نادرست به کاربر نشان داده شود.

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

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

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

تست‌هایی مثل «حذف پستی که قبلاً حذف شده»، «حذف پستی که به آن ارجاع داده شده» و «حذف انبوه» را حتماً بنویسید.

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

در بعد امنیت، چند اصل را همیشه رعایت کنید:

  • پیش از حذف، سطح دسترسی را با capability بررسی کنید
  • nonce را در فرم‌های سفارشی قرار دهید
  • force_delete را فقط در سناریوهای خاص استفاده کنید
  • پیش از حذف انبوه، از پست‌ها بکاپ تهیه کنید

برای مطالعه مباحث امنیتی در سطح دیتابیس، مطلب SQL Injection Prevention در وردپرس مرجع اصلی است.

از منظر عملکرد، هر فراخوانی این تابع باعث اجرای چندین کوئری می‌شود:

  1. یک کوئری DELETE یا UPDATE روی wp_posts
  2. حذف روابط در wp_term_relationships
  3. حذف متادیتا در wp_postmeta
  4. حذف commentها در wp_comments
  5. حذف revisionها

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

در صورت نیاز به مدیریت بهینه دیتابیس پس از حذف، مطلب متد wpdb::prepare و قطعه کد پاک‌سازی داده‌های اضافی را مطالعه کنید.

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

تفاوت wp_delete_post با wp_trash_post چیست؟

wp_trash_post() پست را به زباله‌دان منتقل می‌کند و بازگشت‌پذیر است، درحالی‌که wp_delete_post() با force_delete = true حذف قطعی انجام می‌دهد.

آیا wp_delete_post روی پست‌های سفارشی هم کار می‌کند؟

بله، برای هر post type ثبت‌شده کار می‌کند. اما توجه داشته باشید که هر post type ممکن است متادیتای مخصوص خودش را داشته باشد و باید پاکسازی جداگانه انجام دهید.

آیا می‌توان فایل‌های ضمیمه پست را هم حذف کرد؟

خیر، حذف فایل‌های فیزیکی آپلود‌شده انجام نمی‌شود. برای حذف آن‌ها باید با wp_delete_attachment() و شناسه ضمیمه اقدام کنید.

آیا wp_delete_post باعث حذف commentها می‌شود؟

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

آیا پس از حذف، رد پا در دیتابیس باقی می‌ماند؟

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

آیا wp_delete_post روی Multisite رفتار متفاوتی دارد؟

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

نگاه مهندسی سطح بالا

در سطح معماری، wp_delete_post() را نباید یک عملیات صرفاً حذف دانست. این تابع در واقع یک cascade delete است که درون خود چندین زیرعملیات را هماهنگ می‌کند. برخلاف دیتابیس‌های رابطه‌ای با foreign key constraint که cascade delete را در سطح موتور اجرا می‌کنند، وردپرس این کار را در لایه PHP انجام می‌دهد.

نکته ظریف اول این است که در حالت force_delete = false، وردپرس فیلد post_status را به trash تغییر می‌دهد. این یعنی رکورد اصلی در دیتابیس باقی می‌ماند و تنها با یک کوئری اضافه در WP_Query از فهرست‌ها حذف می‌شود. در دیتابیس‌های بسیار بزرگ، انبوه پست‌های trashed می‌تواند کارایی کوئری‌های عادی را کاهش دهد.

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

نکته سوم، مسئله replication و caching است. اگر از Redis یا Memcached استفاده می‌کنید، حذف پست لزوماً کش را پاک نمی‌کند و ممکن است داده حذف‌شده برای مدتی در کش باقی بماند. در این حالت باید wp_cache_delete() را در hook مناسب فراخوانی کنید. برای آشنایی با کش در وردپرس، مطلب تابع wp_cache_delete را مطالعه کنید.

در نهایت، در پروژه‌های Enterprise توصیه می‌کنم یک لایه Safe Delete بسازید که ابتدا پست را به trashed منتقل کند، پس از بازه بازیابی (مثلاً 30 روز) به‌صورت خودکار با wp_schedule_event حذف قطعی را انجام دهد و در تمام مسیر، رخدادها را لاگ کند.

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