Multiple custom post types using the same taxonomy = URL frustrations

You’ll have to add some new rewrite rules, thankfully WordPress makes it fairly straightforward using a few function calls.

// use the init action to modify rewrite rules
add_action( 'init', function() {
    global $wp_rewrite;

    // add rewrite tag for the post type
    // - %posttype% is a new tag that will be replaced with the regex in the actual query
    // - the regex will extract the post_type name from the URL and use it in the query
    add_rewrite_tag( '%posttype%', '([^/]+)', 'post_type=" );

    // create the new permastruct by combining the post type & taxonomy permastructs
    // - custom taxonomies and post types are added to the extra_permastructs array in WP_Rewrite
    // - change "category' to desired taxonomy
    // - output will be '%posttype%/category/%category%'
    $types_and_cats_permastruct="%posttype%" . $wp_rewrite->get_extra_permastruct( 'category' );

    // add the permastruct for post type & taxonomy
    add_permastruct( 'post_type_and_category', $types_and_cats_permastruct, array(
        'with_front' => true,           //  - with_front (bool) - Should the structure be prepended with WP_Rewrite::$front? Default is true.
        'ep_mask' => EP_ALL_ARCHIVES,   //  - ep_mask (int) - Endpoint mask defining what endpoints are added to the structure. Default is EP_NONE.
        'paged' => true,                //  - paged (bool) - Should archive pagination rules be added for the structure? Default is true.
        'feed' => true,                 //  - feed (bool) - Should feed rewrite rules be added for the structure? Default is true.
        'forcomments' => false,         //  - forcomments (bool) - Should the feed rules be a query for a comments feed? Default is false.
        'walk_dirs' => false,           //  - walk_dirs (bool) - Should the 'directories' making up the structure be walked over and rewrite
                                        //    rules built for each in turn? Default is true.
        'endpoints' => true             //  - endpoints (bool) - Should endpoints be applied to the generated rewrite rules? Default is true.
    ) );

} );

So to explain what I’m doing above:

  1. Adding a rewrite tag with add_rewrite_tag() so that when you put the custom post type name into the URL (eg. /events/location/uk/) it will be recognised by the regular expression and the resulting query WP runs would contain post_type=events
  2. Making a new permastruct to generate rules for using the tag we just added and the existing taxonomy permastruct, in your case this would be location so /event/location/uk will show uk events and /business/location/uk will show uk businesses
  3. Adding the new permastruct with add_permastruct() so that WP generates the new rewrite rules whenever permalinks are refreshed. In the 3rd parameter it’s very important to have walk_dirs set to false as you’ll get some undesirable aggressive rewrite rules with archives for /%posttype%/category/ and /%posttype%/

Gotchas

  • Generating links – if you want to use the_terms() to output location links for a business or event that only shows the originating post type you’ll have to filter the URLs or write a custom function to output URLs to match your permastruct
  • If you never want an archive listing both events and businesses then 2 separate taxonomies is a better idea as you’ll more easily be able to tell if a term has any posts in it eg. when generating links and deciding whether to output a link to an empty archive or not. In that case @richard-howarth’s answer is correct

Leave a Comment