After creating Custom post type by user delete old one

I have created a form where users update their profile. When a profile is created or updated it creates a CPT called course. The url is the permalink + /course/ + the title of the course

/*  CREATING COURSE PAGES FROM USER PROFILES */

function create_course_page( $user_id = '' ) {
   $user = new WP_User($user_id);
   if ( ! $user-ID ) return '';

   // check if the user whose profile is updating has already a post
   global $wpdb;
   $course_post_exists = $wpdb-get_var( $wpdb-prepare(
   SELECT ID FROM $wpdb-posts WHERE post_name = %s AND post_type = 'course' and post_status = 'publish', $user-course_name
   ) );

  if ( ! in_array('instructor', $user-roles) ) return '';

  $user_info = array_map( function( $a ){ return $a[0]; }, get_user_meta( $user-ID ) );
  $title = $user_info['course_name'];
  // of course create the content as you want
  $content = 'This is the page for: ';
  $content .= $user_info['description_course'];
  $post = array(
    'post_title' = $title,
    'post_name' = $user-course_name,
    'post_content' = $content,
    'post_status' = 'publish',
    'post_type' = 'course'
  );
  if ( $course_post_exists ) {
    $post['ID'] = $course_post_exists;
    wp_update_post( $post );
  } else {
    wp_insert_post( $post );
  }
}

add_action( 'personal_options_update', 'create_course_page' );
add_action( 'edit_user_profile_update', 'create_course_page' );

The problem that I'm facing is that when someone changes the title of the course it creates a new post with this url.

So I want to delete the old post when a title change is done on the course.

NOTE: When changing title of the course it changes the URL, that is why I think it takes like a new post.

I have customised this code a bit to do the job:

/* REMOVE OLD COURSES WHEN UPDATE */
add_action( 'admin_init', 'removing_older_posts' );
function removing_older_posts() {
    $args = array (
        'post_type'         = 'course',
        'author'            = get_current_user_id(),
        'orderby'           = 'date',
        );
    $query = new WP_Query( $args );

    if ( $query-have_posts() ) { //if the current user has posts under his/her name
        $i = 0; //create post counter
        while ( $query-have_posts() ) { //loop through each post
            $query-the_post(); //get the current post
            if ($i  0) { //if you're not on the first post
                wp_delete_post( $query-post-ID, true ); //delete the post
            }
            $i++; //increment the post counter
        }
        wp_reset_postdata();
    }
}

But it isn't working.

Appreciate any suggestion :)

Topic wp-delete-post url-rewriting php custom-post-types Wordpress

Category Web


You don't need to do any of that, and can sidestep the problem completely, and make the code faster and simpler!

This is what we're going to do:

  • We're going to use the course post author, not the name, it's more reliable
  • We're going to avoid the raw SQL query so we can use caches and use WP_Query
  • We'll use wp_insert_post for both updating and creating the post
  • Eliminate a useless array_map call
  • Actually check if wp_insert_post was succesful or not
  • We'll apply the WP coding standards to make it easier to read and nicer to work with, e.g. modern PHP arrays [] rather than array(), strict in_array checks, no global variables
  • The code that figures out the course ID will be moved to its own function

This way, when the course name changes, the original course post has its title and URL updated/changed, rather than creating a brand new course that requires cleanup of old courses.

Here's something close to what should work:

function get_course_by_author( $user_id ) : int {
    // if we don't find anything then a value of 0 will create a new post rather than update an existing post.
    $course_id = 0;
    $args = [
        'post_type'      => 'course',
        'posts_per_page' => 1,
        'post_author'    => $user_id,
    ];
    $query     = new WP_Query( $args );
    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            $course_id = get_the_ID();
            // we only want a single post ID
            break;
        }
        // cleanup after the query
        wp_reset_postdata();
    } else {
        // perhaps the course was created before post authors were set? Fall back to looking for the post by its slug/name
        return 0;
    }
    return $course_id;
}

function get_course_by_slug( string $slug ) : int {
    $course_id = 0;
    $args = [
        'post_type'      => 'course',
        'posts_per_page' => 1,
        'post_name'      => $slug,
    ];
    ... etc etc see get_course_by_author ...
    return $course_id;
}

function handle_course_post( $user_id = '' ) {
    // First, is this user an instructor?
    $user = get_user_by( 'ID', $user_id );
    if ( ! $user || ! in_array( 'instructor', $user->roles, true ) ) {
        return;
    }

    // get the course name
    $course_name = get_user_meta( $user_id, 'course_name', true );
    if ( empty( $course_name ) {
        error_log( "the user does not have a course_name!" );
    }

    // Then lets find this users course if it exists.
    // if we don't find anything then a value of 0 will create a new post rather than update an existing post.
    $course_id = get_course_by_author( $user_id );
    if ( $course_id === 0 ) {
        // we didn't find a course with that author, try the course name.
        $course_id = get_course_by_slug( $course_name );
    }

    // Finally, save/update the course!
    $content  = 'This is the page for: ';
    $content .= get_user_meta( $user_id, 'description_course', true );
    $args     = [
        'ID'           => $course_id,
        'post_title'   => $course_name,
        'post_name'    => $course_name, // set the slug/permalink
        'post_content' => $content,
        'post_status'  => 'publish',
        'post_type'    => 'course',
    ];
    $post_id  = wp_insert_post( $args );

    // check if we were succesful.
    if ( is_wp_error( $post_id ) ) {
        // it didn't work!
        error_log( $post_id->get_error_message() );
    } else if ( empty( $post_id ) ) {
        // it didn't work!
        error_log( "post_id is empty/false" );
    }

}

add_action( 'personal_options_update', 'handle_course_post' );
add_action( 'edit_user_profile_update', 'handle_course_post' );

Remember this is untested and will require some understanding, it can't be copy pasted as is ( although if it does work as is then yay! ).

Notice that at the end we check if the call to wp_insert_post worked or not. We can't assume it was succesful, what if another user has already chosen that course name? What if the user is no longer an instructor?

Also keep in mind that your existing course posts do not have the right author, they will need to be re-saved. Once this is done all cases will be handled. The code above will try to fetch the course post via its slug if it can't find it via the author, allowing a partial migration path.

This does mean your original problem will still happen, but only to users who have never updated their profile, and immediatley change their course name. Those who save their profiles then change the name will be handled, as will all future users.


I think it would be helpful if you could explain why you're generating courses out of edits that a user's doing on his profile. The simplest solution seems to be a frontend form that just allows a user to create new posts or edit existing ones rather than going for this alternative solution where you're using the data from the profile page to create new posts.


If you want to stick to the current implementation, I believe it would be useful for you to look into status transitions: https://developer.wordpress.org/reference/hooks/transition_post_status/

Here's some example code that I used to create a post in a the category with the ID 1 (= news) after a custom post type had been created:

// Automatically create a news when a wiki post has been published
function nxt_create_news($new_status, $old_status, $nxt_wiki_post) {
    if('publish' === $new_status && 'publish' !== $old_status && $nxt_wiki_post->post_type === 'wiki') {
        $nxt_post_author = $nxt_wiki_post->post_author;
        $nxt_wiki_permalink = get_post_permalink($nxt_wiki_id);
        $nxt_wiki_title = $nxt_wiki_post->post_title;
        $nxt_postarr = array(
            'post_author' => $nxt_post_author,
            'post_content' => 'The wiki entry ' . $nxt_wiki_title . ' has just been published.<br /><br /><a href="' . $nxt_wiki_permalink . '" title="' . $nxt_wiki_title . '">Check out wiki entry</a>.',
            'post_title' => 'The wiki entry ' . $nxt_wiki_title . ' has been published',
            'post_status' => 'publish',
            'post_category' => array(1),
        );
        $nxt_post_id = wp_insert_post($nxt_postarr);
        set_post_thumbnail(get_post($nxt_post_id), '1518');
    }
}
add_action( 'transition_post_status', 'nxt_create_news', 10, 3 );

In your specific case, you can compare parts of the saved posts to the new post that would be created and then prevent the default action from happening (a new post being created) and instead just edit the old post.

The main question would be how would the system know which post someone's trying to edit? I can't really answer that as I couldn't make out why you're using the profile edits to create new posts I'm afraid, but I hope this is helpful regardless. :-)

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.