最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Custom post type permalink changing itself every other change

programmeradmin2浏览0评论

I have created a parent > child > grandchild structure. (Courses > Series > Episodes).

I have created the below code which updates the urls for each of the custom post types so that I can have the url structure

/%course%/%series%/%episode%.

For example, a course called 'crash course', with a series of 'the basics', with an episode of 'checking your mirrors' would look like this

Everything is working perfectly fine, but i've noticed a very weird bug. Every other time I save an episode, the permalink changes itself to try and match a random series?

For example, if I save the episode once, my permalink is

.

Then as soon as I go back into the episode, make any changes and try to save it again, once i've saved and the page refreshes, it changes itself to

, and I have to click edit and manually change it back.

My full custom post type registration code is below, please let me know if anyone has any ideas! Its not ideal to be changing the permalink back manually every other time I make a change!

Note - My apologies for the huge code snippet below, i thought it would be best if I pasted ALL of my custom post type code in to help debug!

<?php
/* ----------------------------------------------------------------------------------------------------------
        Register our custom post types
---------------------------------------------------------------------------------------------------------- */
function create_post_type() {
    //Courses
    register_post_type( 'course',
      [
        'labels' => array(
          'name' => __( 'Courses' ),
          'singular_name' => __( 'Course' )
        ),
        'description' => 'All courses',
        'public' => true,
        'hierarchical' => true,
        'rewrite' => array(
            'slug'       => 'system',
        ),
        'menu_icon' => 'dashicons-welcome-learn-more',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );

    //Series
    register_post_type( 'series',
      [
        'labels' => array(
          'name' => __( 'Series' ),
          'singular_name' => __( 'Series' )
        ),
        'description' => 'Course Series',
        'public' => true,
        'hierarchical' => false,
        'show_ui'              => true,
        'show_in_menu'         => 'edit.php?post_type=course',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );

    //Episodes
    register_post_type( 'episodes',
      [
        'labels' => array(
          'name' => __( 'Episodes' ),
          'singular_name' => __( 'Episode' )
        ),
        'description' => 'Series Episodes',
        'public' => true,
        'hierarchical' => false,
        'show_ui'              => true,
        'show_in_menu'         => 'edit.php?post_type=course',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );
}
add_action( 'init', 'create_post_type' );



/* ----------------------------------------------------------------------------------------------------------
        Add our meta boxes to our series and episode add/edit page
---------------------------------------------------------------------------------------------------------- */
function my_add_meta_boxes() {
  //Series Meta Boxes
  add_meta_box( 'series-parent', 'Course', 'series_attributes_meta_box', 'series', 'side', 'high' );

  //Episode Meta Boxes
  add_meta_box( 'episode-parent', 'Series', 'episode_attributes_meta_box', 'episodes', 'side', 'high' );
}
add_action( 'add_meta_boxes', 'my_add_meta_boxes' );

function series_attributes_meta_box($post) {
  $post_type_object = get_post_type_object($post->post_type);
    $pages = wp_dropdown_pages([
    'post_type' => 'course',
    'selected' => $post->post_parent,
    'name' => 'parent_id',
    'show_option_none' => __( '(no parent)' ),
    'sort_column'=> 'menu_order, post_title',
    'echo' => 0
  ]);
  if (!empty($pages)){
        echo $pages;
    }
}

function episode_attributes_meta_box( $post ) {
  $post_type_object = get_post_type_object( $post->post_type );
  $select = "<select name='parent_id' id='parent_id'>";
    $select .= "<option value=''>(No Parent)</option>";
    //Get Series
    $args = [
      'post_type' => 'series',
      'posts_per_page' => -1,
      'meta_key' => 'series_number',
      'orderby' => 'meta_value_num',
      'order' => 'ASC'
    ];
    $seriesQuery = new WP_Query($args);
    //Loop the series
    while($seriesQuery->have_posts()){
      $seriesQuery->the_post();
      $select .= "<option " . (get_the_ID() == $post->post_parent ? 'selected' : '') . " class='level-0' value='" . get_the_ID() . "'>" . get_the_title(wp_get_post_parent_id(get_the_ID())) . ' - ' . get_the_title() . "</option>";
    }
    wp_reset_postdata();
  $select .= "</select>";
  echo $select;
}


/* ----------------------------------------------------------------------------------------------------------
        String replace on the permalinks before we save them
---------------------------------------------------------------------------------------------------------- */
function custom_permalinks($permalink, $post, $leavename) {

  $post_id = $post->ID;
  $parent = $post->post_parent;
  $parent_post = get_post( $parent );

  //Series or Episodes
  switch($post->post_type){
    case 'episodes':
        if(empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
          return $permalink;
        } else {
          $grandparent_post = get_post( $parent_post->post_parent );
          $search = ['%course%', '%series%'];
          $replace = [$grandparent_post->post_name, $parent_post->post_name];
          $permalink = str_replace($search, $replace, $permalink);
          return $permalink;
        }
    break;
    case 'series':
        if(empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
          return $permalink;
        } else {
          $search = ['%course%'];
          $replace = [$parent_post->post_name];
          $permalink = str_replace($search, $replace, $permalink);
          return $permalink;
        }
    break;
    default:
        return $permalink;
    break;
  }

}
add_filter('post_type_link', 'custom_permalinks', 10, 3);

function my_add_rewrite_rules() {

  //Episodes
  add_permastruct('episodes', '/system/%course%/%series%/%episodes%', false,  ['walk_dirs' => false]);
  add_rewrite_tag('%episodes%', '([^/]+)', 'episodes=');
  add_rewrite_rule('^system/([^/]+)/([^/]+)/([^/]+)?','index.php?episodes=$matches[3]','top');

  //Series
  add_permastruct('series', '/system/%course%/%series%', false,  ['walk_dirs' => false]);
    add_rewrite_tag('%series%', '([^/]+)', 'series=');
  add_rewrite_rule('^system/([^/]+)/([^/]+)/?','index.php?series=$matches[2]','top');


}
add_action( 'init', 'my_add_rewrite_rules' );

?>

I have created a parent > child > grandchild structure. (Courses > Series > Episodes).

I have created the below code which updates the urls for each of the custom post types so that I can have the url structure

http://mywebsite.co.uk/system/%course%/%series%/%episode%.

For example, a course called 'crash course', with a series of 'the basics', with an episode of 'checking your mirrors' would look like this

http://mywebsite.co.uk/system/crash-course/the-basics/checking-your-mirrors

Everything is working perfectly fine, but i've noticed a very weird bug. Every other time I save an episode, the permalink changes itself to try and match a random series?

For example, if I save the episode once, my permalink is

http://mywebsite.co.uk/system/crash-course/the-basics/checking-your-mirrors.

Then as soon as I go back into the episode, make any changes and try to save it again, once i've saved and the page refreshes, it changes itself to

http://mywebsite.co.uk/system/crash-course/the-basics/the-basics-2, and I have to click edit and manually change it back.

My full custom post type registration code is below, please let me know if anyone has any ideas! Its not ideal to be changing the permalink back manually every other time I make a change!

Note - My apologies for the huge code snippet below, i thought it would be best if I pasted ALL of my custom post type code in to help debug!

<?php
/* ----------------------------------------------------------------------------------------------------------
        Register our custom post types
---------------------------------------------------------------------------------------------------------- */
function create_post_type() {
    //Courses
    register_post_type( 'course',
      [
        'labels' => array(
          'name' => __( 'Courses' ),
          'singular_name' => __( 'Course' )
        ),
        'description' => 'All courses',
        'public' => true,
        'hierarchical' => true,
        'rewrite' => array(
            'slug'       => 'system',
        ),
        'menu_icon' => 'dashicons-welcome-learn-more',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );

    //Series
    register_post_type( 'series',
      [
        'labels' => array(
          'name' => __( 'Series' ),
          'singular_name' => __( 'Series' )
        ),
        'description' => 'Course Series',
        'public' => true,
        'hierarchical' => false,
        'show_ui'              => true,
        'show_in_menu'         => 'edit.php?post_type=course',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );

    //Episodes
    register_post_type( 'episodes',
      [
        'labels' => array(
          'name' => __( 'Episodes' ),
          'singular_name' => __( 'Episode' )
        ),
        'description' => 'Series Episodes',
        'public' => true,
        'hierarchical' => false,
        'show_ui'              => true,
        'show_in_menu'         => 'edit.php?post_type=course',
        'supports' => ['title', 'custom_fields', 'page-attributes']
      ]
    );
}
add_action( 'init', 'create_post_type' );



/* ----------------------------------------------------------------------------------------------------------
        Add our meta boxes to our series and episode add/edit page
---------------------------------------------------------------------------------------------------------- */
function my_add_meta_boxes() {
  //Series Meta Boxes
  add_meta_box( 'series-parent', 'Course', 'series_attributes_meta_box', 'series', 'side', 'high' );

  //Episode Meta Boxes
  add_meta_box( 'episode-parent', 'Series', 'episode_attributes_meta_box', 'episodes', 'side', 'high' );
}
add_action( 'add_meta_boxes', 'my_add_meta_boxes' );

function series_attributes_meta_box($post) {
  $post_type_object = get_post_type_object($post->post_type);
    $pages = wp_dropdown_pages([
    'post_type' => 'course',
    'selected' => $post->post_parent,
    'name' => 'parent_id',
    'show_option_none' => __( '(no parent)' ),
    'sort_column'=> 'menu_order, post_title',
    'echo' => 0
  ]);
  if (!empty($pages)){
        echo $pages;
    }
}

function episode_attributes_meta_box( $post ) {
  $post_type_object = get_post_type_object( $post->post_type );
  $select = "<select name='parent_id' id='parent_id'>";
    $select .= "<option value=''>(No Parent)</option>";
    //Get Series
    $args = [
      'post_type' => 'series',
      'posts_per_page' => -1,
      'meta_key' => 'series_number',
      'orderby' => 'meta_value_num',
      'order' => 'ASC'
    ];
    $seriesQuery = new WP_Query($args);
    //Loop the series
    while($seriesQuery->have_posts()){
      $seriesQuery->the_post();
      $select .= "<option " . (get_the_ID() == $post->post_parent ? 'selected' : '') . " class='level-0' value='" . get_the_ID() . "'>" . get_the_title(wp_get_post_parent_id(get_the_ID())) . ' - ' . get_the_title() . "</option>";
    }
    wp_reset_postdata();
  $select .= "</select>";
  echo $select;
}


/* ----------------------------------------------------------------------------------------------------------
        String replace on the permalinks before we save them
---------------------------------------------------------------------------------------------------------- */
function custom_permalinks($permalink, $post, $leavename) {

  $post_id = $post->ID;
  $parent = $post->post_parent;
  $parent_post = get_post( $parent );

  //Series or Episodes
  switch($post->post_type){
    case 'episodes':
        if(empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
          return $permalink;
        } else {
          $grandparent_post = get_post( $parent_post->post_parent );
          $search = ['%course%', '%series%'];
          $replace = [$grandparent_post->post_name, $parent_post->post_name];
          $permalink = str_replace($search, $replace, $permalink);
          return $permalink;
        }
    break;
    case 'series':
        if(empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
          return $permalink;
        } else {
          $search = ['%course%'];
          $replace = [$parent_post->post_name];
          $permalink = str_replace($search, $replace, $permalink);
          return $permalink;
        }
    break;
    default:
        return $permalink;
    break;
  }

}
add_filter('post_type_link', 'custom_permalinks', 10, 3);

function my_add_rewrite_rules() {

  //Episodes
  add_permastruct('episodes', '/system/%course%/%series%/%episodes%', false,  ['walk_dirs' => false]);
  add_rewrite_tag('%episodes%', '([^/]+)', 'episodes=');
  add_rewrite_rule('^system/([^/]+)/([^/]+)/([^/]+)?','index.php?episodes=$matches[3]','top');

  //Series
  add_permastruct('series', '/system/%course%/%series%', false,  ['walk_dirs' => false]);
    add_rewrite_tag('%series%', '([^/]+)', 'series=');
  add_rewrite_rule('^system/([^/]+)/([^/]+)/?','index.php?series=$matches[2]','top');


}
add_action( 'init', 'my_add_rewrite_rules' );

?>
Share Improve this question asked Apr 24, 2019 at 18:13 S_RS_R 1635 bronze badges 2
  • Does it happen every time you edit an episode? – Krzysiek Dróżdż Commented Apr 25, 2019 at 4:41
  • Every other time – S_R Commented Apr 25, 2019 at 7:55
Add a comment  | 

1 Answer 1

Reset to default 2

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. :)

发布评论

评论列表(0)

  1. 暂无评论