Στο WordPress, η διαγραφή ενός άρθρου δεν σημαίνει και διαγραφή των αρχείων που το συνοδεύουν. Όταν σβήνουμε ένα post, ένα προϊόν ή οποιοδήποτε custom post type, η χαρακτηριστική εικόνα (featured image) παραμένει στη Media Library. Αυτό είναι default συμπεριφορά και δεν είναι bug.
Σε μικρά sites, αυτό συνήθως περνά απαρατήρητο. Σε sites όμως με όγκο περιεχομένου, editorial ομάδες ή e-commerce εγκαταστάσεις, το πρόβλημα δεν είναι αμελητέο. Αντίθετα, συσσωρεύεται αργά και αθόρυβα, μέχρι να γίνει τεχνικό βάρος.
Η επιλογή του WordPress να μην διαγράφει attachments μαζί με το post είναι συνειδητή. Ένα αρχείο μπορεί να χρησιμοποιείται:
Αν το σύστημα διέγραφε αυτόματα τα αρχεία, θα υπήρχε σοβαρός κίνδυνος διαγραφής αρχείου ή εικόνας η οποία χρησιμοποιείται αλλού. Αυτό σημαίνει ότι το WordPress ακολουθει την ασφαλή οδό. Δεν γνωρίζει το context κάθε site, οπότε αφήνει την ευθύνη στον developer.
Σε production περιβάλλοντα με μεγάλο αριθμό posts, τα orphaned attachments αρχίζουν να έχουν κόστος:
Το πρόβλημα δεν είναι ότι “πιάνει χώρο”. Είναι ότι χαλάει την καθαρότητα του συστήματος.
Η πιο ασφαλής και ελεγχόμενη προσέγγιση δεν είναι να σβήνουμε όλα τα attachments ενός post. Είναι να σβήνουμε μόνο τη featured image, με την προϋπόθεση ότι:
Σε editorial sites και blogs, αυτό είναι συχνά απολύτως λογικό.
Η διαγραφή πρέπει να γίνεται μόνο όταν το post διαγράφεται οριστικά, όχι όταν πηγαίνει στα διεγραμμένα (Trash). Ευτυχώς το WordPress παρέχει hooks που μας επιτρέπουν να επέμβουμε ακριβώς σε αυτή τη στιγμή.
Ο παρακάτω κώδικας χρησιμοποιεί το hook before_delete_post και διαγράφει τη featured image όταν το post διαγράφεται οριστικά.
add_action('before_delete_post', 'px_delete_featured_image_with_post_safe');
function px_delete_featured_image_with_post_safe($post_id) {
$thumbnail_id = get_post_thumbnail_id($post_id);
if (!$thumbnail_id) {
return;
}
// Έλεγχος: χρησιμοποιείται αυτή η εικόνα ως featured image αλλού;
$q = new WP_Query([
'post_type' => 'any',
'post_status' => 'any',
'posts_per_page' => 1, // μας αρκεί να βρούμε 1
'fields' => 'ids',
'meta_query' => [
[
'key' => '_thumbnail_id',
'value' => (string) $thumbnail_id,
],
],
'post__not_in' => [$post_id], // αγνοούμε το post που διαγράφεται
'no_found_rows' => true,
]);
if ($q->have_posts()) {
// Χρησιμοποιείται αλλού, δεν τη διαγράφουμε.
return;
}
// Δεν βρέθηκε αλλού ως featured image, άρα είναι ασφαλές να τη σβήσουμε.
wp_delete_attachment($thumbnail_id, true);
}Παρακάτω είναι μια εκδοχή που στοχεύει μόνο προϊόντα WooCommerce (product) και, όταν διαγράφεται οριστικά το προϊόν, διαγράφει:
Και σε κάθε περίπτωση, πριν σβήσει attachment κάνει έλεγχο αν χρησιμοποιείται αλλού ως featured image σε άλλο post/product, ώστε να μη “σπάσεις” άλλο περιεχόμενο.
add_action('before_delete_post', 'px_wc_delete_product_images_on_delete');
function px_wc_delete_product_images_on_delete($post_id) {
// Μόνο WooCommerce products
if (get_post_type($post_id) !== 'product') {
return;
}
// Αν για κάποιο λόγο δεν υπάρχει WC (π.χ. απενεργοποιήθηκε), μην κάνεις τίποτα
if (!class_exists('WooCommerce')) {
return;
}
// WooCommerce product object
$product = wc_get_product($post_id);
if (!$product) {
return;
}
$attachment_ids = [];
// 1) Featured image
$thumbnail_id = (int) get_post_thumbnail_id($post_id);
if ($thumbnail_id) {
$attachment_ids[] = $thumbnail_id;
}
// 2) Product gallery images
$gallery_ids = $product->get_gallery_image_ids();
if (!empty($gallery_ids)) {
foreach ($gallery_ids as $gid) {
$gid = (int) $gid;
if ($gid) {
$attachment_ids[] = $gid;
}
}
}
// 3) (Προαιρετικό αλλά χρήσιμο) Variation images
// Αν το product είναι variable, κάθε variation μπορεί να έχει δική του εικόνα.
if ($product->is_type('variable')) {
foreach ($product->get_children() as $variation_id) {
$variation_product = wc_get_product($variation_id);
if (!$variation_product) {
continue;
}
$vid = (int) $variation_product->get_image_id();
if ($vid) {
$attachment_ids[] = $vid;
}
}
}
// Καθάρισε duplicates
$attachment_ids = array_values(array_unique(array_filter($attachment_ids)));
if (empty($attachment_ids)) {
return;
}
// Διαγραφή attachments με safeguard:
// Σβήνουμε μόνο όσα ΔΕΝ χρησιμοποιούνται αλλού ως featured image.
foreach ($attachment_ids as $attachment_id) {
if (px_attachment_is_used_as_featured_elsewhere($attachment_id, $post_id)) {
continue;
}
wp_delete_attachment($attachment_id, true);
}
}
/**
* Ελέγχει αν ένα attachment χρησιμοποιείται ως featured image σε άλλο post/product.
* Σημείωση: Δεν καλύπτει χρήση μέσα στο content ή σε custom fields.
*/
function px_attachment_is_used_as_featured_elsewhere($attachment_id, $current_post_id) {
$q = new WP_Query([
'post_type' => 'any',
'post_status' => 'any',
'posts_per_page' => 1,
'fields' => 'ids',
'meta_query' => [
[
'key' => '_thumbnail_id',
'value' => (string) (int) $attachment_id,
],
],
'post__not_in' => [(int) $current_post_id],
'no_found_rows' => true,
]);
return $q->have_posts();
}Με αυτόν τον κώδικα μειώνεις δραματικά τον κίνδυνο να σπάσει άλλο περιεχόμενο. ΣΗΜΑΝΤΙΚΟ: Πρέπει πάντα λαμβάνουμε υπόψιν οτι κάθε ιστοσελίδα είναι σχεδιασμένη διαφορετικά και σε κάθε περίπτωση αυτό θα πρέπει να γίνεται με προσοχή και πιθανότατα σε συνεννόηση με τον άνθρωπο ή την εταιρεία που έφτιαξε την ιστοσελίδα για να αποφύγουμε δυσάρεστα αποτελέσματα.
Αυτός ο έλεγχος προστατεύει από reuse ως featured image (ή και galleries στην περίπτωση των προϊόντων), αλλά δεν μπορεί να εγγυηθεί ότι η εικόνα δεν χρησιμοποιείται αλλού (π.χ. μέσα σε περιεχόμενο, ACF fields, builders κλπ).