Thêm chức năng xóa bài viết là xóa ảnh cho website WordPress

Khi xây dựng một website WordPress, đặc biệt là các trang thương mại điện tử với hàng ngàn sản phẩm, bạn có thể dễ dàng gặp phải tình trạng “rác” ảnh tồn đọng. Điều này xảy ra khi bạn xóa bài viết hoặc sản phẩm, nhưng các hình ảnh liên quan lại không được xóa theo, vẫn nằm chễm chệ trên hosting.


Vấn Đề Gặp Phải Khi Xóa Bài Viết/Sản Phẩm Nhưng Ảnh Vẫn Còn

Việc ảnh không được xóa cùng bài viết/sản phẩm có thể gây ra nhiều rắc rối:

  1. Tốn dung lượng hosting: Đây là vấn đề dễ thấy nhất. Mỗi hình ảnh, đặc biệt là các biến thể ảnh được WordPress tự động tạo ra (thumbnail, medium, large, v.v.), đều chiếm dụng không gian lưu trữ. Với các website có hàng ngàn sản phẩm, hàng chục ngàn bài viết, dung lượng ảnh không sử dụng có thể lên tới hàng chục GB, gây lãng phí tài nguyên và có thể khiến bạn phải nâng cấp gói hosting không cần thiết.
  2. Giảm hiệu suất website: Mặc dù ảnh không sử dụng trực tiếp làm chậm trang web người dùng, nhưng việc có quá nhiều file rác trên hosting có thể ảnh hưởng đến hiệu suất tổng thể của server, làm chậm các tác vụ quản trị, sao lưu, và quét virus.
  3. Khó quản lý Media Library: Thư viện media của bạn sẽ trở nên lộn xộn với hàng ngàn hình ảnh không còn liên quan đến bất kỳ nội dung nào. Việc tìm kiếm ảnh sẽ khó khăn hơn, và bạn có thể vô tình sử dụng lại những ảnh không còn phù hợp.
  4. Đặc biệt nghiêm trọng với WooCommerce:
    • Sản phẩm nhiều mẫu mã, biến thể: Một sản phẩm có thể có nhiều hình ảnh chính và mỗi biến thể (ví dụ: kích thước, màu sắc) lại có thêm hình ảnh riêng. Khi bạn xóa một sản phẩm hoặc một biến thể, tất cả những hình ảnh này có nguy cơ bị bỏ lại, nhân lên số lượng ảnh rác một cách chóng mặt.
    • Thị trường cạnh tranh cao: Trong các ngành kinh doanh đòi hỏi cập nhật sản phẩm liên tục (thời trang, điện tử, v.v.), việc thêm/xóa sản phẩm diễn ra thường xuyên. Nếu không có cơ chế dọn dẹp ảnh tự động, website sẽ nhanh chóng phình to không kiểm soát.

Giải Pháp: Tự Động Xóa Ảnh Khi Xóa Bài Viết/Sản Phẩm

Để giải quyết vấn đề này, chúng ta cần một chức năng tự động xóa các file hình ảnh liên quan khỏi hosting khi một bài viết (post, page, custom post type như sản phẩm WooCommerce) được xóa vĩnh viễn.

Dưới đây là đoạn code bạn có thể thêm vào file functions.php của Child Theme.

Quan trọng: Luôn sao lưu file functions.php và toàn bộ website trước khi thêm code!

Cấu hình cho Hosting Yếu (Sử dụng hàng đợi để xóa dần)

Đối với các hosting có tài nguyên hạn chế, việc xóa hàng trăm hoặc hàng nghìn hình ảnh cùng lúc có thể gây quá tải server, dẫn đến lỗi timeout hoặc website không truy cập được. Để khắc phục, chúng ta sẽ sử dụng một hệ thống hàng đợi đơn giản để xóa ảnh từ từ.

Ý tưởng: Khi bài viết bị xóa, thay vì xóa ảnh ngay lập tức, chúng ta sẽ lưu ID của các file ảnh cần xóa vào một tùy chọn (option) trong database. Sau đó, WordPress Cron Job sẽ chạy định kỳ để xử lý một số lượng ảnh nhất định trong hàng đợi.

<?php
/**
 * File functions.php của Child Theme.
 * Chứa các tùy chỉnh và chức năng bổ sung.
 */

// Nhúng stylesheet của Parent Theme và Child Theme
function my_theme_enqueue_styles() {
    wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
    wp_enqueue_style( 'child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style'), wp_get_theme()->get('Version') );
}
add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_styles' );


/* --- Bắt đầu code Xóa Ảnh theo Hàng Đợi --- */

/**
 * Hằng số: Số lượng ảnh tối đa sẽ xóa mỗi lần Cron Job chạy.
 * Điều chỉnh giá trị này tùy theo sức mạnh của hosting.
 * Nếu hosting yếu, giảm số này xuống (ví dụ: 5, 10).
 */
define( 'MAX_IMAGES_TO_DELETE_PER_RUN', 20 ); // Xóa 20 ảnh mỗi lần Cron chạy

/**
 * Tên option trong database để lưu hàng đợi ảnh cần xóa.
 */
define( 'IMAGE_DELETE_QUEUE_OPTION_NAME', 'images_to_delete_queue' );


/**
 * 1. Hàm thêm ID ảnh vào hàng đợi khi bài viết bị xóa vĩnh viễn
 */
function enqueue_post_attachments_for_deletion( $post_id ) {
    // Chỉ xử lý khi bài viết bị xóa vĩnh viễn, không phải khi chuyển vào thùng rác
    if ( ! current_user_can( 'delete_posts' ) ) {
        return; // Đảm bảo người dùng có quyền xóa bài viết
    }

    // Lấy tất cả các file đính kèm của bài viết
    $attachments = get_attached_media( 'image', $post_id );

    if ( $attachments ) {
        $attachment_ids = array_keys( $attachments ); // Lấy chỉ các ID
        
        // Lấy hàng đợi hiện có
        $queue = get_option( IMAGE_DELETE_QUEUE_OPTION_NAME, array() );
        
        // Thêm các ID mới vào hàng đợi
        $queue = array_merge( $queue, $attachment_ids );
        
        // Cập nhật hàng đợi vào database (loại bỏ trùng lặp nếu có)
        update_option( IMAGE_DELETE_QUEUE_OPTION_NAME, array_unique( $queue ) );
    }
}
// Hook vào hành động khi bài viết bị xóa vĩnh viễn (bao gồm cả Post, Page, Custom Post Type)
add_action( 'before_delete_post', 'enqueue_post_attachments_for_deletion' );


/**
 * 2. Đăng ký WordPress Cron Job để xử lý hàng đợi
 */
function schedule_image_deletion_cron() {
    // Đảm bảo Cron Job chỉ được lên lịch một lần
    if ( ! wp_next_scheduled( 'delete_queued_images_daily' ) ) {
        wp_schedule_event( time(), 'hourly', 'delete_queued_images_daily' ); // Chạy mỗi giờ
    }
}
add_action( 'wp', 'schedule_image_deletion_cron' );

/**
 * 3. Hàm xử lý xóa ảnh từ hàng đợi
 */
function process_queued_images_for_deletion() {
    $queue = get_option( IMAGE_DELETE_QUEUE_OPTION_NAME, array() );

    if ( empty( $queue ) ) {
        return; // Không có gì trong hàng đợi
    }

    // Lấy một số lượng ảnh nhất định từ đầu hàng đợi để xử lý
    $images_to_process = array_slice( $queue, 0, MAX_IMAGES_TO_DELETE_PER_RUN );
    $remaining_queue = array_slice( $queue, MAX_IMAGES_TO_DELETE_PER_RUN );

    foreach ( $images_to_process as $attachment_id ) {
        // Kiểm tra xem đây có thực sự là một file đính kèm ảnh và có tồn tại không
        if ( wp_attachment_is_image( $attachment_id ) && get_post_status( $attachment_id ) ) {
            // Xóa file đính kèm và tất cả các phiên bản của nó trên hosting
            // true: xóa file trên host, false: chỉ xóa khỏi database
            wp_delete_attachment( $attachment_id, true ); 
        }
    }

    // Cập nhật hàng đợi sau khi đã xử lý xong
    update_option( IMAGE_DELETE_QUEUE_OPTION_NAME, $remaining_queue );
}
// Hook hàm xử lý vào Cron Job đã đăng ký
add_action( 'delete_queued_images_daily', 'process_queued_images_for_deletion' );


/**
 * 4. Hủy lịch trình Cron Job khi theme bị vô hiệu hóa (quan trọng để tránh lỗi)
 */
function unschedule_image_deletion_cron() {
    $timestamp = wp_next_scheduled( 'delete_queued_images_daily' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'hourly', 'delete_queued_images_daily' );
    }
}
// Hook vào khi Child Theme bị deactivate
register_deactivation_hook( __FILE__, 'unschedule_image_deletion_cron' );

/* --- Kết thúc code Xóa Ảnh theo Hàng Đợi --- */

Giải Thích Chi Tiết Code:

  1. MAX_IMAGES_TO_DELETE_PER_RUN: Hằng số này định nghĩa số lượng ảnh tối đa sẽ được xóa trong mỗi lần Cron Job chạy.
    • Lưu ý: Bạn cần điều chỉnh giá trị này dựa trên tài nguyên hosting của bạn.
      • Hosting yếu: Đặt giá trị thấp (ví dụ: 5 hoặc 10).
      • Hosting mạnh: Có thể đặt cao hơn (ví dụ: 50 hoặc 100).
    • Việc này giúp tránh tình trạng quá tải server.
  2. IMAGE_DELETE_QUEUE_OPTION_NAME: Hằng số này định nghĩa tên của một tùy chọn (option) trong database WordPress của bạn. Chúng ta sẽ sử dụng tùy chọn này để lưu trữ các ID của hình ảnh cần xóa vào một “hàng đợi”.
  3. enqueue_post_attachments_for_deletion( $post_id ):
    • Hàm này được kích hoạt bởi hook before_delete_post. Hook này chạy ngay trước khi một bài viết (bao gồm cả Post, Page, và Custom Post Types như Product của WooCommerce) bị xóa vĩnh viễn khỏi database.
    • Nó lấy tất cả các file đính kèm kiểu image của bài viết đang bị xóa.
    • Sau đó, lấy danh sách các ID của những ảnh này và thêm chúng vào một mảng queue (hàng đợi).
    • Mảng queue này được lưu trữ trong database dưới dạng một tùy chọn (option), tên được định nghĩa bởi IMAGE_DELETE_QUEUE_OPTION_NAME. Hàm array_unique() đảm bảo không có ID ảnh nào bị trùng lặp trong hàng đợi.
  4. schedule_image_deletion_cron():
    • Hàm này đăng ký một Cron Job của WordPress. Cron Job này sẽ chạy định kỳ (trong ví dụ này là hourly – mỗi giờ).
    • Tên hook của Cron Job là delete_queued_images_daily.
  5. process_queued_images_for_deletion():
    • Đây là hàm chính xử lý việc xóa ảnh. Nó được kích hoạt mỗi khi Cron Job delete_queued_images_daily chạy.
    • Hàm này đọc hàng đợi ảnh từ database.
    • Nó lấy một số lượng ảnh nhất định từ đầu hàng đợi (được xác định bởi MAX_IMAGES_TO_DELETE_PER_RUN).
    • Với mỗi attachment_id trong số đó, nó kiểm tra xem đó có phải là một hình ảnh hợp lệ và tồn tại không.
    • Sử dụng wp_delete_attachment( $attachment_id, true ) để xóa hình ảnh khỏi database và cả khỏi hosting. Tham số true thứ hai là để đảm bảo file thực tế cũng bị xóa.
    • Sau khi xử lý xong một lô ảnh, hàm sẽ cập nhật lại hàng đợi trong database, loại bỏ những ảnh vừa xóa.
  6. unschedule_image_deletion_cron():
    • Hàm này được kích hoạt khi Child Theme của bạn bị vô hiệu hóa (deactivate).
    • Nó có nhiệm vụ hủy bỏ lịch trình Cron Job đã đăng ký. Điều này là quan trọng để tránh việc Cron Job vẫn cố gắng chạy khi theme không còn hoạt động, gây ra lỗi.

Cách Triển Khai:

  1. Sao lưu website: Bước quan trọng nhất.
  2. Mở file functions.php của Child Theme của bạn (thường là wp-content/themes/your-child-theme/functions.php).
  3. Dán toàn bộ đoạn code từ /* --- Bắt đầu code Xóa Ảnh theo Hàng Đợi --- */ đến /* --- Kết thúc code Xóa Ảnh theo Hàng Đợi --- */ vào cuối file functions.php, ngay trước thẻ đóng ?> (nếu có).
  4. Điều chỉnh MAX_IMAGES_TO_DELETE_PER_RUN: Thay đổi giá trị 20 thành một số phù hợp với tài nguyên hosting của bạn. Nếu không chắc, hãy bắt đầu với một số nhỏ như 5 hoặc 10 và tăng dần nếu thấy ổn định.
  5. Lưu file functions.php và tải lên hosting.
  6. Xóa Cache: Hãy xóa mọi loại cache bạn đang sử dụng (plugin cache, hosting cache, CDN cache) để đảm bảo code mới được kích hoạt.
  7. Kiểm tra: Xóa thử một bài viết hoặc sản phẩm trên website của bạn. Sau đó, chờ khoảng 1 giờ (hoặc khoảng thời gian bạn đã đặt cho Cron Job chạy) và kiểm tra lại thư mục wp-content/uploads/ của bạn để xem ảnh đã được xóa chưa. Bạn cũng có thể kiểm tra trong database ở bảng wp_options để xem tùy chọn images_to_delete_queue có xuất hiện và rỗng dần không.

Lưu Ý Quan Trọng:

  • WordPress Cron Job không phải Cron Job hệ thống: WP-Cron chỉ chạy khi có ai đó truy cập website của bạn. Nếu website có ít lượt truy cập, Cron Job có thể không chạy thường xuyên. Đối với các website lớn hoặc cần độ chính xác cao về thời gian, bạn nên vô hiệu hóa WP-Cron trong wp-config.php và thiết lập Cron Job hệ thống thực sự trên hosting (thường thông qua cPanel/FastPanel), trỏ đến wp-cron.php.
  • Không ảnh hưởng đến file media không đính kèm: Chức năng này chỉ xóa các file ảnh được đính kèm vào bài viết/sản phẩm. Nếu bạn tải ảnh lên mà không đính kèm vào bất kỳ đâu, hoặc ảnh được dùng trong các plugin slider, builder không theo chuẩn WordPress, chúng sẽ không bị xóa.
  • Thận trọng khi chỉnh sửa database: Mặc dù code này an toàn, nhưng việc tự động xóa file luôn tiềm ẩn rủi ro nếu có lỗi. Luôn đảm bảo bạn có bản sao lưu trước khi thực hiện.
  • Kiểm tra logs: Nếu gặp vấn đề, hãy kiểm tra các file error log của hosting để tìm thông báo lỗi liên quan.

Việc triển khai chức năng này sẽ giúp website của bạn gọn gàng, tiết kiệm dung lượng và duy trì hiệu suất tốt hơn về lâu dài, đặc biệt là với các website WooCommerce có quy mô lớn.

Xem thêm: Hướng Dẫn Tự Tạo Child Theme (Theme Con) cho WordPress

Rate this post
Chia sẻ tới bạn bè và gia đình
kiendinh
kiendinh

Đinh Hùng Kiên, từng "thử sức" với C++, C#, nhưng lại rẽ hướng sang Marketing. Với tư duy logic sắc bén từ nền tảng lập trình và sự hỗ trợ mạnh mẽ của AI (ChatGPT, Gemini), Kiên chuyên về gỡ lỗi, triển khai các tính năng website. Kiên lập codecungai.com để giúp những người không chuyên biến ý tưởng thành hiện thực trên WordPress một cách dễ dàng.

Bài viết: 12
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất
Phản hồi nội tuyến
Xem tất cả bình luận
0
Rất thích suy nghĩ của bạn, hãy bình luận.x