Multiple post types – share same ReWrite slug?

I’ve run into another wonderful 404 issue. I’m trying to have 2 seperate post types that share the same rewrite slug. I’ve flushed my rewrite rules, and when I test only 1 of the CPT works- the other gets a 404. My rewrite that I wish to use for both is:

'rewrite' => array('slug' => 'team/%teamtype%'),

Anyone know how to handle this?

Read More
add_action( 'init', 'create_rider_type' );

function create_rider_type() {

register_post_type('riders', 
array(  
    'description' => 'rider custom post type',
    'show_ui' => true,
    'menu_position' => 5,
    'menu_icon' => get_stylesheet_directory_uri() . '/images/riders.png',
    'exclude_from_search' => false,
    'labels' => array(
        'name' => 'Team Riders',
        'singular_name' => 'Rider',
        'add_new' => 'Add New rider',
        'add_new_item' => 'Add New rider',
        'edit' => 'Edit riders',
        'edit_item' => 'Edit rider',
        'new_item' => 'New rider',
        'view' => 'View rider',
        'view_item' => 'View rider',
        'search_items' => 'Search riders',
        'not_found' => 'No riders found',
        'not_found_in_trash' => 'No riders found in Trash',
        'parent' => 'Parent rider',
    ),
    'hierarchical' => false,
    'supports' => array('title','editor','excerpt', 'trackbacks','custom-fields', 'comments','revisions','thumbnail','author','page-attributes'),
    'public' => true,
    'rewrite' => array('slug' => 'team/%teamtype%'),
    'taxonomies' => array('teamtype')
    ) 
);


}





add_action( 'init', 'create_sponsor_type' );

function create_sponsor_type() {

register_post_type('sponsors', 
array(  
    'description' => 'sponsor custom post type',
    'show_ui' => true,
    'menu_position' => 5,
    'menu_icon' => get_stylesheet_directory_uri() . '/images/sponsors.png',
    'exclude_from_search' => false,
    'labels' => array(
        'name' => 'Team sponsors',
        'singular_name' => 'sponsor',
        'add_new' => 'Add New sponsor',
        'add_new_item' => 'Add New sponsor',
        'edit' => 'Edit sponsors',
        'edit_item' => 'Edit sponsor',
        'new_item' => 'New sponsor',
        'view' => 'View sponsor',
        'view_item' => 'View sponsor',
        'search_items' => 'Search sponsors',
        'not_found' => 'No sponsors found',
        'not_found_in_trash' => 'No sponsors found in Trash',
        'parent' => 'Parent sponsor',
    ),
    'hierarchical' => false,
    'supports' => array('title','editor','excerpt', 'trackbacks','custom-fields', 'comments','revisions','thumbnail','author','page-attributes'),
    'public' => true,
    'rewrite' => array('slug' => 'team/%teamtype%'),
    'taxonomies' => array('teamtype')
    ) 
);

}

*************Update***********************************

The original CPT rewrite code I posted was simplified so I could get more straight to the point- however maybe it makes more sense if you can see how i’m handling those permalinks with my custom Taxonomy. I’ve updated the code to show.

I really wish to keep them as seperate post types- for organization as well as seperate metaboxes for each. Please check the updated rewrites for the CPT’s, as well as my Taxonomy setup below:

add_action( 'init', 'create_team_taxonomies' );

function create_team_taxonomies() {
register_taxonomy( 
'teamtype', 
array('riders','sponsors'),
array( 
    'labels' => array(
        'name' => 'Team Types',
        'singular_name' => 'Team Type',
        'search_items' =>  'Search Team Types',
        'popular_items' => 'Popular Team Types',
        'all_items' => 'All Team Types',
        'parent_item' => 'Parent Team Type',
        'parent_item_colon' => 'Parent Team Type:',
        'edit_item' => 'Edit Team Type', 
        'update_item' => 'Update Team Type',
        'add_new_item' => 'Add New Team Type',
        'new_item_name' => 'New Team Type Name'
    ), 
'hierarchical' => true, 
'public' => true,
'show_ui' => true,
'query_var' => 'teamtype',
'show_tagcloud' => true,
'rewrite' => array( 'slug' => 'team', 'with_front' => false) 
) 
);

}

And here is how I setup the rewrite when I select the Taxonomy, and publish the post:

add_filter('post_link', 'teamtypepermalink', 10, 3);
add_filter('post_type_link', 'teamtypepermalink', 10, 3);

function teamtypepermalink($permalink, $post_id, $leavename) {
if (strpos($permalink, '%teamtype%') === FALSE) return $permalink;

    // Get post
    $post = get_post($post_id);
    if (!$post) return $permalink;

    // Get taxonomy terms
    $terms = wp_get_object_terms($post->ID, 'teamtype');    
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
    else $taxonomy_slug = 'not-specified';

return str_replace('%teamtype%', $taxonomy_slug, $permalink);
}

Related posts

Leave a Reply

5 comments

  1. I’ve just done a project where two Custom Post Types needed to share the same slug. The trick is to overwrite the query vars via the request filter. Let’s call the post types ‘foo’ and ‘bar’, and they will share slug ‘foo’.

    The following code is rough and from memory for example only. Salt to taste, do not copy-paste:

    add_filter('request', 'overwriteQueryVars', 10, 1);
    
    function overwriteQueryVars($query)
    {
        if ( empty($query['post_type']) || $query['post_type']!='foo' ) {
            return $query;
        }
    
        // Run an SQL query that looks like this, against both post types
        $sql = "SELECT ID FROM wp_posts WHERE post_type='foo' and post_name='%s'";
        $post_name = urlencode($query['name']);
    
        // If you've found that the row exists for post_type 'bar' but not 'foo',
        // then simply return the $query array unaltered
        if ( in_foo_but_not_bar ) {
            return $query;
        }
    
        // If you've found that the row exists for post_type 'bar' but not 'foo',
        // then rewrite the $query array:
    
        // Original:
        $query = array (
            'page' => '',
            'foo' => 'some-nice-post',
            'post_type' => 'foo',
            'name' => 'some-nice-post',
        );
    
        // Make it look like this:
        $query = array (
            'page' => '',
            'bar' => 'some-nice-post',
            'post_type' => 'bar',
            'name' => 'some-nice-post',
        );
    
    }
    
  2. I know this question is very old, but when I was looking for a solution to a similar problem, this question came closest to what I was trying to achieve, and since it came up in my search I figured there might not yet be any better solutions available. So I’ll add the solution I came up with below in case it might help someone later on with a similar issue.

    I was having a similar problem. I wanted to have a new custom post type that would share the slug with another post type. In my case I had a post type called country-info and I wanted to use the same permalink structure as the standard page post type.

    In other words I wanted it to be accessable using example.com/page-name/ instead of example.com/country-info/page-name.

    This is how I solved it:

    // I found it easiest to grab the request in the parse_request hook. There you can see
    // the requested slug, as well as if the requested page existed or not.
    
    add_action('parse_request', 'overwriteRequest', 10, 1);
    function overwriteRequest($query) {
    
        // If the request matched an existing page, the query_vars array will contain the
        // post_type, but otherwise it will only contain an error.
        // So we only execute our code if the post_type is empty
        // On the top page, the query_vars is an empty array, so we check for that too!
        if ( count($query->query_vars) > 0 && empty($query->query_vars['post_type'])) {
    
            // The request contains the string that was requested.
            $pageName = $query->request;
            $postType = 'country-info';
    
            // Check if post with the requested slug exists in your custom post type
            $result = get_posts(array(
                'post_type' => $postType,
                'name' => $pageName
            ));
    
            // If it doesn't, just return the query
            if (count($result) < 1) {
    
                return $query;
    
            // If it does, create a new query_vars array to replace the old one.
            } else {
                $new_query = array(
                    'page' => '',
                    'country-info' => $pageName,
                    'post_type' => $postType,
                    'name' => $pageName
                );
                $query->query_vars = $new_query;
                return $query;
            }
    
        } else {
            return $query;
        }
    }
    

    Please note:

    In my case the requests I was looking for where all at root level of the domain like example.com/page-name, but if your custom post type is within a directory like example.com/some-directory/post-name/, then you will need to change one thing in the code:

    $pageName = $query->request;

    needs to be

    $pageName = preg_replace('/my-directory-name//', '', $query->request)

    Otherwise get_posts will try to find a post where the slug contains your directory, which will always give you zero results. This preg_replace simply removes your directory name from the string.

    Remember to replace my-directory-name with the real directory name!

    This solution is tested and works on WordPress 4.9.7

  3. As far as I know, you can’t do that. Every slug is converted to a query_vars item that queries a specific object type (taxonomy or post type). If you analice your WP_Query, you’ll see that is querying the first post type.

    Maybe a WP Ninja here can enlighten us with this, but I’m almost sure that you can’t do it. Or at least, you shouldn’t.

  4. As CPTs (and posts / pages, for that matter) are constructed, that’s not possible. The slug is reserved for the specific type, with the understanding / assumption that 2 CPTs = 2 different types of content.

    based on your code, it almost seems like each of the items you have in the CPT would be better be served as a custom taxonomy, with both being attached to a single CPT (I’m guessing some sort of event?)

  5. Your rewrite rules will overwrite each other, and WordPress will query for only one of the post types. I think it is the latest one that is registered that “wins”, so /testslug/bmw/ will always query for a post with slug bmw and post type sponsors. If you go to /testslug/some-name/ and some-name is a rider, it will still query for post type sponsors and post slug some-name. It will find no post, and thus give you a 404. This happens in the “default” query that WordPress will do for you, before it loads the template page, so you can’t solve this in your single-sponsors.php template file for example (you could in 404.php, but then it gets ugly).

    The cleanest solution is to replace part of the logic that does this URL parsing, and make it accept an “ambiguous” URL. Test whether it is a rider or a sponsor, and change the parameters so WordPress will continue with either a rider or a sponsor. Mike once provided a way to do this with pages and taxonomies, it should be possible to extend this to work with posts only.