I have this code in functions.php that organize media upload according to its Custom Post Type (CPT).

So all images uploaded to a "Product" CPT will be inside wp-content/uploads/product directory.

add_filter("upload_dir", function ($args) {
  $id = (isset($_REQUEST["post_id"]) ? $_REQUEST["post_id"] : "");

  if($id) {
    $newdir = "/" . get_post_type($id);

    // remove default dir
    $args["path"] = str_replace( $args["subdir"], "", $args["path"]);
    $args["url"] = str_replace( $args["subdir"], "", $args["url"]);

    // assign new dir
    $args["subdir"] = $newdir;
    $args["path"] .= $newdir; 
    $args["url"] .= $newdir;

    return $args;

It works well, except when I delete the media, the file is still there (the database entry is deleted just fine).

I figured I need to filter the media deletion too, but can't seem to find the right way. Have anyone successfully set this up?



I tried adding conditional to use default folder when the post type is post.

if(get_post_type($id) === "post") {
  return $args;
} else {

Deleting a Post's media also won't delete the file.


A small mistake, the return should be outside if

add_filter("upload_dir", function ($args) {
  $id = (isset($_REQUEST["post_id"]) ? $_REQUEST["post_id"] : "");

  if($id) {
    $newdir = "/" . get_post_type($id);

  return $args;

We did something quite similar in our plugin recently. Here's how we handle media deletions only when one of our posts is being deleted.

 * Delete all attached media when a product is deleted
function product_delete_attached_media( $post_id ) {

    // If it's not a product being deleted, we don't want to do anything
    if ( 'product' != get_post_type( $post_id ) )

    // Setup the arguments for a custom Query
    $args = array(
        'post_type' => 'attachment', // We want attachments...
        'posts_per_page' => -1, // ... all of them ...
        'post_status' => 'any', // ... no matter if public, in trash etc. ...
        'post_parent' => $post_id // ... that are a child of the product being deleted here!

    // Make acustom query with those arguments to get those attachments
    $attachments = new WP_Query( $args );

    // Loop through each one of them and delete them
    foreach ( $attachments->posts as $attachment ) {
        if ( false === wp_delete_attachment( $attachment->ID, true ) ) {
            // In here you could output or log something if anything went wrong

// We add this function to the "before_delete_post" hook
// which runs before each deletion of a post
add_action( 'before_delete_post', 'product_delete_attached_media' );

The comments should explain what's going on quite well. wp_delete_attachment() function takes care of the deletion of the media files and should successfully delete the files as well as the entries in the database.

The only thing we had to do on top of that was to remove the whole custom folder structure if our plugin get's uninstalled.

Also, i'm pretty sure the WP_Query could be optimized as it could potentially get slow if you have a ton of posts and images.

Hope that helps.

  • 1
    Sorry, i might misread your question. This code will delete all assets attached to a post when the post itself gets deleted. To fix what you described above i would take a look at the 'wp_handle_upload_prefilter' and 'wp_handle_upload' filters. Let me know if that works, maybe i can get you a working example later on. Nov 4 '15 at 19:05
  • Hi, sorry for the late reply. The project that involves this code has been delayed and just started again. It's actually a small mistake on my code, I have write the correct one in my own answer.
    – hrsetyono
    Dec 7 '15 at 9:05
  • Cool - i missed that one, too. Thanks for adding your solution! Dec 8 '15 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.