Bram G Bram G - 5 months ago 19
PHP Question

Wordpress custom post type rewrite rule matches all pages

I've created a custom post type with a rewrite to use the grandparent relationship as the URL like so:

function cpt_child(){
$args = array(
//code
'rewrite' => array( 'slug' => '%grandparent%/%parent%', 'with_front' => false),
);
register_post_type( 'child', $args );
}
add_action( 'init', 'cpt_child' );


Then I update the permalink:

add_filter( 'post_type_link', 'filter_the_post_type_link', 1, 2 );
function filter_the_post_type_link( $post_link, $post ) {
switch( $post->post_type ) {
case 'child':
$post_link = get_bloginfo( 'url' );
$relationship_child = p2p_type('children_to_parents')->get_adjacent_items($post->ID);
$parent = $relationship['parent']->post_name;
$relationship_parent = p2p_type('parents_to_grandparents')->get_adjacent_items($parent['parent']->ID);
$grandparent = $relationship_parent['parent']->post_name;
$post_link .= "/{$grandparent}/";
$post_link .= "{$parent}/";
$post_link .= "{$post->post_name}";
break;
}
return $post_link;
}


This all works great, but unfortunately the rewrite rule matches regular pages as well which makes them 404.

I can prevent this by adding a custom slug, for example 'relationship':
http://example.com/relationship/grandparent/parent/child


But I'd really like to use
http://example.com/grandparent/parent/child
and have it not break regular pages.

I might have to resort to using non native Wordpress rewrites using htaccess.

Thanks in advance!

Answer

I managed to find a solution thanks to Milo's previous answers on other related questions. I found this particular post Remove base slug in CPT & CT, use CT in permalink to be extremely helpful.

My initial CPT with grandparent relationship rewrite remains the same:

function cpt_child(){
 $args = array(
  //code
  'rewrite' => array( 'slug' => '%grandparent%/%parent%', 'with_front' => false),
 );
 register_post_type( 'child', $args );
}
add_action( 'init', 'cpt_child' );

Then I update the permalink:

add_filter( 'post_type_link', 'filter_the_post_type_link', 1, 2 );
function filter_the_post_type_link( $post_link, $post ) {
  switch( $post->post_type ) {
    case 'child':
            $post_link = get_bloginfo( 'url' );
            $relationship_child = p2p_type('children_to_parents')->get_adjacent_items($post->ID);
            $parent = $relationship['parent']->post_name;
            $relationship_parent = p2p_type('parents_to_grandparents')->get_adjacent_items($parent['parent']->ID);
            $grandparent = $relationship_parent['parent']->post_name;
            $post_link .= "/{$grandparent}/";
            $post_link .= "{$parent}/";
            $post_link .= "{$post->post_name}";
    break;
  }
  return $post_link;
}

Hooked into request and changed query based on request vars that match my needs. Added is_admin() check to prevent request filter from changing the CPT's back end.

// URL rewrite pages 404 fix
if ( ! is_admin() ) {
  function svbr_fix_requests( $request ){
    // if it's not a section request and request is not empty treat request as page or post
    if( ( ! array_key_exists( 'section' , $request ) ) && ( ! empty($request) ) ){
            $request['post_type'] = array( 'post', 'page' );
    }

    // return request vars
    return $request;
  }
  add_filter( 'request', 'svbr_fix_requests' );
}

Because we changed the query vars we have to add template functionality.

// Use single_template filter to properly redirect to page.php and custom page templates
function svbr_get_template_file($single_template) {
  global $post;

  $page_custom_template = get_post_meta( $post->ID, '_wp_page_template', true );

  if ($post->post_type == 'page') {
    if($page_custom_template != 'default') {
      // We are using a child theme, so get_stylesheet_directory()
      $single_template = get_stylesheet_directory() . '/' . $page_custom_template;
    }
    else {
      $single_template = get_template_directory() . '/page.php';
    }
  }
  return $single_template;
}
add_filter( 'single_template', 'svbr_get_template_file' );

Last but not least we have to add the template class to the body for styling purposes.

// Add template class to body
add_filter( 'body_class', 'template_class_name' );
function template_class_name( $classes ) {
  global $post;
  $page_custom_template = get_post_meta( $post->ID, '_wp_page_template', true );
  $page_custom_template = str_replace('.php','',$page_custom_template);
  $page_custom_template = 'page-template-' . $page_custom_template;
    // add 'class-name' to the $classes array
    $classes[] = $page_custom_template;
    // return the $classes array
    return $classes;
}
Comments