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
1 Answer
Reset to default 2BETTER 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:
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>"; }
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. :)