Skip Navigation

[Resolved] Delete related posts and their attachments when deleting parent post with cred-delete-post shortcode

This thread is resolved. Here is a description of the problem and solution.

Problem: I would like to use a cred-delete-post shortcode to allow my visitors to delete a post on the front-end of the site. This post may be the parent of one or more child posts. When the parent post is deleted, I would like to delete its attachments, and its child posts as well as their attachments.

Solution: Use the 'delete' action in the cred-delete-post shortcode, and customize the following code snippet to work with your post type slugs and post relationship slug.

function delete_post_children($post_id) {
      global $wpdb;
      $post_type = get_post_type( $post_id );
      if ( $post_type == 'products' || $post_type == 'profile') {
        $ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_parent = $post_id AND post_type = 'attachment'");
        foreach ( $ids as $id ) {
            wp_delete_attachment($id, true);
        }
        if(has_post_thumbnail( $post_id )) {
          $tn_id = get_post_thumbnail_id( $post_id );
          wp_delete_attachment($tn_id, true);
        }
        if( $post_type == 'profile' ) {
          // in this case, we want to force-delete the child product posts as well
          $profile_products = toolset_get_related_posts(
            $post_id,
            'profile-product',
            [
              'query_by_role' => 'parent',
              'role_to_return' => 'child',
              'limit' => 10000,
              'orderby' => null,
            ]
          );
          foreach($profile_products as $profile_product ) {
            wp_delete_post( $profile_product, true );
          }
        }
      }
    }
    add_action('before_delete_post', 'delete_post_children');

Relevant Documentation:
https://toolset.com/documentation/programmer-reference/forms/cred-shortcodes/
https://toolset.com/documentation/customizing-sites-using-php/post-relationships-api/
https://developer.wordpress.org/reference/hooks/before_delete_post/
https://developer.wordpress.org/reference/functions/wp_delete_post/

100% of people find this useful.

This support ticket is created 3 years, 5 months ago. There's a good chance that you are reading advice that it now obsolete.

This is the technical support forum for Toolset - a suite of plugins for developing WordPress sites without writing PHP.

Everyone can read this forum, but only Toolset clients can post in it. Toolset support works 6 days per week, 19 hours per day.

Sun Mon Tue Wed Thu Fri Sat
8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 8:00 – 12:00 - -
13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 13:00 – 17:00 - -

Supporter timezone: America/New_York (GMT-04:00)

This topic contains 10 replies, has 2 voices.

Last updated by Christian Cox 3 years, 4 months ago.

Assisted by: Christian Cox.

Author
Posts
#1847173

Hi guys, some time ago I submitted another similar request which was fulfilled, and now I would like to expand on it:

First, here is the original one: https://toolset.com/forums/topic/proper-post-attachment-management-via-cred/
(here I wanted to delete all files attached to the post being deleted thus not leaving anything on the server behind)

The original solution still works perfectly, however I am missing a function now that I have expanded the functionality of my project and here is where the issue lies:

Now that I am having users register their profile the on platform, then they can add multiple products to their profiles .(*profiles* and *products* being custom post types with a parent>child relationship).
However when the user deletes their profile the system will delete the parent item and all it's file attachments but will break the parent>child relationship and leave the child products with their attachments behind leaving my server clogged and database full of products that don't have parents.

What I expect to happen:
I expect once the user deletes any parent post item the system would have the ability to delete the associated child posts and their associated file attachments from the server too.

Can you help with this?

Thanks!
Diyan

#1847611

Hello, it sounds like you need to add some code that handles the deletion process for products and profiles, queries the related product posts of each deleted profile, and deletes the related product and its attachments as well. A modification like this:

function delete_post_children($post_id) {
  global $wpdb;
  $post_type = get_post_type( $post_id );
  if ( $post_type == 'bag' || $post_type == 'product' || $post_type == 'profile') {
    $ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_parent = $post_id AND post_type = 'attachment'");
    foreach ( $ids as $id ) {
        wp_delete_attachment($id, true);
    }
    if(has_post_thumbnail( $post_id )) {
      $tn_id = get_post_thumbnail_id( $post_id );
      wp_delete_attachment($tn_id, true);
    }
    if( $post_type == 'profile' ) {
      // in this case, we want to force-delete the child product posts as well
      $profile_products = toolset_get_related_posts(
        $post_id,
        'profile-products-relationship-slug',
        [
          'query_by_role' => 'parent',
          'role_to_return' => 'child',
          'limit' => 10000,
          'orderby' => null,
        ]
      );
      foreach($profile_products as $profile_product ) {
        wp_delete_post( $profile_product, true );
      }
    }
  }
}
add_action('before_delete_post', 'delete_post_children');

You may need to replace the post type slugs 'product' and 'profile' in the if statement:

if ( $post_type == 'bag' || $post_type == 'product' || $post_type == 'profile') {

...as well as this if statement:

if( $post_type == 'profile' ) {

...as well as the post relationship slugprofile-products-relationship-slug, to match the actual relationship slug. Note that this code will force-delete the product posts, not just trash them. Let me know if you have questions about this.

#1847641

Hi Chris,

Okay, first this is on hidden link (it is running a copy of the same code here, just no "bags" post type).

I have Users that get registered either via email or login with fb(email again).
Once they login they create profiles. They can have more than one profile.
For each profile they add products.

Here is my code:

function delete_post_children($post_id) {
	  global $wpdb;
	  $post_type = get_post_type( $post_id );
	  if ( $post_type == 'products' || $post_type == 'profile') {
		$ids = $wpdb->get_col("SELECT ID FROM {$wpdb->posts} WHERE post_parent = $post_id AND post_type = 'attachment'");
		foreach ( $ids as $id ) {
			wp_delete_attachment($id, true);
		}
		if(has_post_thumbnail( $post_id )) {
		  $tn_id = get_post_thumbnail_id( $post_id );
		  wp_delete_attachment($tn_id, true);
		}
		if( $post_type == 'profile' ) {
		  // in this case, we want to force-delete the child product posts as well
		  $profile_products = toolset_get_related_posts(
			$post_id,
			'profile-product',
			[
			  'query_by_role' => 'parent',
			  'role_to_return' => 'child',
			  'limit' => 10000,
			  'orderby' => null,
			]
		  );
		  foreach($profile_products as $profile_product ) {
			wp_delete_post( $profile_product, true );
		  }
		}
	  }
	}
	add_action('before_delete_post', 'delete_post_children');

Note: slugs go as follows: "profile", "products", and "profile-product" for the relationship.

Unfortunately the way I got it seems to fail for now, any idea what am I missing?
Thanks!
Diyan

#1847685

The updates seem to be correct at first glance. May I log in and take a look in wp-admin? Please let me know where you added this code, if it is in functions.php or in a custom code snippet somewhere.

#1847731

Okay let me run some tests locally to see what the problem could be. I will give you an update shortly.

#1847779

It turns out the toolset_get_related_posts API does not allow you to query for related posts of Trashed posts. I've asked my developers if there is a workaround for this problem, and I'll let you know what I find out. If you'd like to perform this action when trashing a post instead of deleting a post, there may be another option. However, the child posts would be deleted if a parent post was mistakenly trashed. This seems like a bad idea to me, but I thought I would ask for your opinion.

#1847781

My goal is to allow users delete their own profiles(not trash them) via front-end.

[cred-delete-post action='trash' onsuccess='14' style="background: red; color:white; border-radius: 4px; padding: 7px 12px;"]Изтрий този Профил[/cred-delete-post]

Maybe I got this action wrong here. .. let me test with delete.
D.

#1847793

One workaround would be to replace the cred-delete-post shortcode with the shortcode for a cred edit form that edits the parent Profile post. Remove all the visible inputs from the edit Form except the submit button, include a generic hidden field with a value like 'delete-profile', and change the text of the submit button to be "Delete Profile". Then you could trigger these Product post delete actions with the cred_save_data hook, in a conditional based on the value of the generic hidden field. Since the Profile post status would not be 'trash' yet, the post relationships api should be able to query the child posts. Then after deleting the Product posts, you could delete the Profile post in the same cred_save_data hook.

Your thoughts on this workaround? It might help bridge the gap until our developers can provide more feedback.

#1847795

Chris, this got fixed once I changed the action to "delete" on my button. Product got deleted, product photo got deleted from the server too.
So maybe your code works after all?

D.

#1847805

OK great, yes I assume since the status of the post never goes to 'trash', the problem does not occur. That's good to know - I'll keep this in mind for future tickets. Thanks for the update.

#1860489

For future reference, a filter is available to allow 'trash' status posts to be both included in the response of, and queried by, the toolset_get_related_posts API. The filter can be added like so:

add_filter( 'toolset_accepted_post_statuses_for_api', static function( $accepted_post_statuses ) {
    if ( is_array( $accepted_post_statuses ) ) { 
        $accepted_post_statuses[] = 'trash';
    }
    return $accepted_post_statuses;
} );

Then in the API arguments section, you can include 'trash' as one of the acceptable post_status values:

$parent_ids = toolset_get_related_posts(
  123,
  'book-chapter',
  [
    'query_by_role' => 'child',
    'role_to_return' => 'other',
    'args' => array(
      'post_status' => 'publish,trash'
    )
  ]
);
This ticket is now closed. If you're a WPML client and need related help, please open a new support ticket.