1

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/'.

Rewrite rules inspector results for episodes enter image description here

  • 1
    You could give Rewrite Rules Inspector a try to see what's happening? – Alexander Holsgrove Apr 23 at 14:39
  • I've installed it but i've never used that plugin before, i'm not quite sure what i'm looking for. I will put what is being displayed for episodes in the question, incase it helps. – S_R Apr 23 at 14:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.