I need to have 3 custom post types in my wordpress site, each one being a parent of another.
Courses > Series > Episodes
I have fully created a functions file that registers the custom post types and rewrites urls. It all works great, except episodes just returns 'Page Not Found'.
The way it works at the moment is a user creates a course. Then when they create a series, there is an extra meta box to select the course its a child of. Then they create an episode, and on the episode page there is an extra meta box to select the series its a child of.
There is a function that re-writes the permalink on the page to create string replace tags in the URL, then when the post is saved, the strings are replaced with the post names of the courses and series.
<?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/courses',
),
'menu_icon' => 'dashicons-welcome-learn-more',
'supports' => ['title', 'custom_fields', 'page-attributes']
]
);
//Series
register_post_type( 'series',
[
'labels' => array(
'name' => __( 'Series' ),
'singular_name' => __( 'Series' )
),
'show_ui' => true,
'show_in_menu' => 'edit.php?post_type=course',
'description' => 'Course series',
'hierarchical' => false,
'public' => true,
'supports' => ['title', 'custom_fields']
]
);
//Episodes
register_post_type( 'episodes',
[
'labels' => array(
'name' => __( 'Episodes' ),
'singular_name' => __( 'Episode' )
),
'show_ui' => true,
'show_in_menu' => 'edit.php?post_type=course',
'description' => 'Series Episodes',
'hierarchical' => false,
'public' => true,
'supports' => ['title', 'custom_fields']
]
);
}
add_action( 'init', 'create_post_type' );
/* ----------------------------------------------------------------------------------------------------------
Add our meta boxes to our series and episode add/edit page
---------------------------------------------------------------------------------------------------------- */
function my_add_meta_boxes() {
add_meta_box( 'series-parent', 'Course', 'series_attributes_meta_box', 'series', 'side', 'high' );
add_meta_box( 'episodes-parent', 'Series', 'episodes_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( array( '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 episodes_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;
}
/* ----------------------------------------------------------------------------------------------------------
Rewrite permalinks
---------------------------------------------------------------------------------------------------------- */
function my_add_rewrite_rules() {
//Series
add_rewrite_tag('%series%', '([^/]+)', 'series=');
add_permastruct('series', 'system/%course%/%series%', [ 'walk_dirs' => false ]);
add_rewrite_rule('^series/([^/]+)/([^/]+)/([^/]+)/?','index.php?series=$matches[1]','top');
//Episodes
add_rewrite_tag('%episodes%', '([^/]+)', 'episode=');
add_permastruct('episodes', 'system/%course%/%series%/%episodes%', [ 'walk_dirs' => false ]);
add_rewrite_rule('^episodes/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?','index.php?episodes=$matches[1]','top');
}
add_action( 'init', 'my_add_rewrite_rules' );
/* ----------------------------------------------------------------------------------------------------------
String replace on the permalinks before we save them
---------------------------------------------------------------------------------------------------------- */
function series_permalinks($permalink, $post, $leavename) {
$post_id = $post->ID;
if($post->post_type != 'series' || empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
return $permalink;
}
$parent = $post->post_parent;
$parent_post = get_post( $parent );
$permalink = str_replace('%course%', $parent_post->post_name, $permalink);
return $permalink;
}
add_filter('post_type_link', 'series_permalinks', 10, 3);
function episodes_permalinks($permalink, $post, $leavename) {
$post_id = $post->ID;
if($post->post_type != 'episodes' || empty($permalink) || in_array($post->post_status, array('draft', 'pending', 'auto-draft'))){
return $permalink;
} else {
$parent_post = get_post( $post->post_parent );
$grandparent_post = get_post( $parent_post->post_parent );
$permalink = str_replace('%course%', $grandparent_post->post_name, $permalink);
$permalink = str_replace('%series%', $parent_post->post_name, $permalink);
return $permalink;
}
}
add_filter('post_type_link', 'episodes_permalinks', 10, 3);
?>
Truthfully a lot of the code has been sourced from numerous stack overflow posts and online guides to get it in its current position.
My issue now is whenever I create an episode the permalink for the episode doesn't work (even after re-flushing my permalinks). It just returns 'page not found'.
Am I doing something wrong with the permalink rewrite?
Note - the 'system/' rewrite is necessary, I am using the multiple theme plugin so everything has to begin with 'system/'.