Custom post type permalink changing itself every other change

BETTER SOLUTION

Actually, there is a note on the WP_Query reference:

Note: Ticket #18408 For querying posts in the admin, consider using
get_posts()
as
wp_reset_postdata()
might not behave as expected.

So instead of attempting to reset the global post data, the more proper solution is either:

  1. Use get_posts():

    $seriesPosts = get_posts($args);
    foreach($seriesPosts as $p){
      $select .= "<option " . ($p->ID == $post->post_parent ? 'selected' : '') . " class="level-0" value="" . $p->ID . "">" . get_the_title(wp_get_post_parent_id($p->ID)) . ' - ' . $p->post_title . "</option>";
    }
    
  2. Or loop through the $seriesQuery->posts:

    $seriesQuery = new WP_Query($args);
    foreach($seriesQuery->posts as $p){
      $select .= "<option " . ($p->ID == $post->post_parent ? 'selected' : '') . " class="level-0" value="" . $p->ID . "">" . get_the_title(wp_get_post_parent_id($p->ID)) . ' - ' . $p->post_title . "</option>";
    }
    

And do not call setup_postdata() in the loop.


ORIGINAL ANSWER

The problem is that the wp_reset_postdata() fails to reset the global $post object, because the post property of the global $wp_query object is a null (i.e. $GLOBALS['wp_query']->post is empty):

function episode_attributes_meta_box( $post ) {
    ...
    wp_reset_postdata(); // <- fails to reset $GLOBALS['post']
    ...
}

So an easy fix is, instead of using wp_reset_postdata(), you can copy/backup the $post object passed to your meta box callback and then set the global $post to that copy at the end of the function (or wherever you think necessary to reset the global post data):

function episode_attributes_meta_box( $post ) {
    $_post = $post; // copy it to $_post

    $post_type_object = get_post_type_object( $post->post_type );
    $select = "<select name="parent_id" id='parent_id'>";
    ... your code ...
    //wp_reset_postdata();
    $select .= "</select>";
    echo $select;

    // Here resets the global post data.
    $GLOBALS['post'] = $_post;
    unset( $_post );
}

There might be other options, but the above trick worked well for me. 🙂

Leave a Comment