Eliminare correttamente post con meta e allegati
Sto gestendo una panoramica di custom post types. Questi hanno tassonomie personalizzate e anche un allegato.
Nella mia panoramica devo fornire link per eliminare le voci. Con questo devo anche eliminare l'allegato e i metadati.
Stavo usando questo codice:
// Verifica i permessi dell'utente
if ( !current_user_can( 'delete_bkroadkill', $post->ID ) )
return;
// Crea il link per l'eliminazione
$link = "<a href='" . wp_nonce_url( get_bloginfo('url') . "/wp-admin/post.php?action=delete&post=" . $post->ID, 'delete-post_' . $post->ID) . "'>".$link."</a>";
echo $before . $link . $after;
Ho trovato Delete Post Link to delete post, its meta and attachments ma non viene fornita alcuna soluzione.
Questo codice elimina solo il post, nient'altro. Qual è il modo corretto per farlo?
Utilizzo questa funzione per eliminare i media associati a un articolo. Se vuoi testarla su un determinato tipo di articolo puoi includere la variabile global $post_type
. In sostanza, ottiene tutti gli allegati e li elimina uno per uno. Riferimento
function delete_associated_media( $id ) {
$media = get_children( array(
'post_parent' => $id,
'post_type' => 'attachment'
) );
if( empty( $media ) ) {
return;
}
foreach( $media as $file ) {
wp_delete_attachment( $file->ID );
}
}
add_action( 'before_delete_post', 'delete_associated_media' );

Anche se funziona, suggerirei di creare un array di ID da eliminare, poi costruire una query personalizzata ed eseguirla. La tua risposta eseguirà più query per eliminare gli allegati; forse non è un problema per 3 o 4, ma se hai 100 allegati potrebbe rallentare notevolmente le operazioni.

Questo codice non esegue più query del tuo. Esegue una query per ottenere gli allegati, poi 1 query per eliminarli, tutto tramite WordPress invece di usare SQL personalizzato.

Non corretto. Tu esegui una query al database per ottenere tutti gli allegati e poi una query per allegato quando li elimini; 10 allegati = 11 query in totale. Io eseguo una query per ottenere tutti gli allegati e una per eliminare tutti i post associati; 10 allegati = 2 query. Va notato però che il tuo codice elimina anche il file, mentre il mio si limita a rimuovere la riga incriminata dal database. Immagino sia una questione di preferenze personali, ma per le prestazioni lo farei a modo mio e poi eseguirei qualche operazione di pulizia per rimuovere gli allegati non associati (un semplice plugin potrebbe gestirlo).

Capisco cosa intendi. Pensavo che l'obiettivo fosse rimuovere completamente gli allegati quando un articolo veniva eliminato.

L'autore del post potrebbe aver inteso quello, suppongo dipenda dall'interpretazione. Dalla mia esperienza precedente ho notato che tenere lontane dagli utenti le attività di manutenzione potenzialmente dispendiose in termini di tempo contribuisce a una migliore esperienza, quindi l'ho interpretato in quel modo.

@s_ha_dum suggerisce che i meta dei post verranno eliminati automaticamente. Pertanto, dato che la sua reputazione indica che sa di cosa parla, questa soluzione gestisce solo gli allegati dei post.
Suggerirei di consultare la documentazione per l'hook before_delete_post(), poiché è molto utile poter verificare quale tipo di post viene eliminato, ecc.
add_action('before_delete_post', 'delete_post_attachments');
function delete_post_attachments($post_id){
global $post_type;
if($post_type !== 'my_custom_post_type') return;
global $wpdb;
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'posts_per_page' => -1,
'post_parent' => $post_id
);
$attachments = new WP_Query($args);
$attachment_ids = array();
if($attachments->have_posts()) : while($attachments->have_posts()) : $attachments->the_post();
$attachment_ids[] = get_the_id();
endwhile;
endif;
wp_reset_postdata();
if(!empty($attachment_ids)) :
$delete_attachments_query = $wpdb->prepare('DELETE FROM %1$s WHERE %1$s.ID IN (%2$s)', $wpdb->posts, join(',', $attachment_ids));
$wpdb->query($delete_attachments_query);
endif;
}
Un'importante nota dalla documentazione citata -
È importante notare che l'hook viene eseguito solo quando l'utente di WordPress svuota il Cestino. Se usi questo hook tieni presente che non verrà attivato se l'utente sta eliminando un Allegato, poiché gli allegati vengono eliminati definitivamente, cioè non inviati al Cestino. Invece usa l'hook delete_post().
Un'altra nota
Dovrei menzionare che mentre il codice in questa risposta eliminerà tutte le righe dal database relative agli allegati del post, in realtà non eliminerà gli allegati stessi.
La mia motivazione per questo è la performance. A seconda del numero di allegati che hai, eliminarli uno alla volta potrebbe richiedere del tempo. Suggerisco che è meglio eliminare inizialmente solo le voci del database per tutti gli allegati per migliorare l'esperienza utente, e poi eseguire una pulizia separata in un altro momento per eliminare gli allegati effettivi (è abbastanza facile cercare ed eliminare i file non associati). In sostanza, meno query + meno lavoro durante l'esperienza utente = meno tempo.

Ho aggiornato la mia risposta (ovviamente sostituisci my_custom_post_type
con il tipo di post rilevante). Inoltre, controlla il link nel post per l'hook before_delete_post(), è tutto spiegato lì.
