Correctly delete posts with meta and attachments

This question already has an answer here:

  • Delete Associated Media Upon Page Deletion

    2 answers

Solutions Collecting From Web of "Correctly delete posts with meta and attachments"

@s_ha_dum suggests that Post meta will be deleted automatically. Therefore because his reputation suggests he knows what he is talking about, this solution only handles Post attachments.

I’d suggest checking out the docs for the before_delete_post() hook, as it’s quite handy to be able to check out what Post Type is being deleted, etc.

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();

    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));


An important note from the aforementioned docs –

It’s important to note the hook runs only when the WordPress user empties the Trash. If you using this hook note that it will not fire if the user is deleting an Attachment, since attachments are force deleted, i.e., not sent to the Trash. Instead use the delete_post() hook.

Another note

I should mention that while the code in this answer will delete all rows from the database related to Post attachments, it will not actually delete the attachments themselves.

My reasoning for this is performance. Depending on the number of attachments that you have, deleting them one at a time could take a while. I suggest it is better to to only delete the database entries for all attachments initially to enhance the user experience, and then run some separate house keeping at another time to delete the actual attachments (it’s easy enough to search for and delete and non-associated files). Basically, less queries + less work during the user experience = less time.

I use this to delete associated media with post. If you want to test against a certain post type you can include the global $post_type variable. Pretty much it gets all attachments and deletes them one by one. Reference

function delete_associated_media( $id ) {
    $media = get_children( array(
        'post_parent' => $id,
        'post_type'   => 'attachment'
    ) );

    if( empty( $media ) ) {

    foreach( $media as $file ) {
        wp_delete_attachment( $file->ID );
add_action( 'before_delete_post', 'delete_associated_media' );