Taxonomy rewrite question

The question:

I have custom post types for dining, lodging, and activities. Each of those custom post types uses a custom taxonomy called region. If, for example, I have a region called “downtown”, I would like to be able to show dining option for that region by going to domain.com/downtown/dining. Or show hotels in that region by going to domain.com/downtown/lodging. So, basically, I like to create a rewrite rule to give the structure domain.com/tax-term/cpt-slug to show all the posts for the cpt that have that tax term selected. Any way to do this?

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Your permalink structure for taxonomy will conflict with default one for pages.

In the case of the address domain.com/item-one/item-two how does WordPress know if it’s a page item-two with the parent page item-one or item-two is custom post type and item-one is term of region taxonomy?
The order of rewrite rules will decide, and because rule for your structure will be first, links to child pages (like domain.com/parent-page/child-page) will be directed to taxonomy page (taxonomy= “region”, term= “parent-page”, post_type= “child-page”).

There are two ways, of which I would choose the first.

  • Add some prefix to your structure e.g. regions/{term}/{cpt_slug}

  • Generate rewrite rules for all terms (downtown and others) from region taxonomy (one rule for each term).
    Generating must be repeated after adding and removing the custom taxonomy term.

In both cases, to make rewrite rules work, you have to click Save in Dashboard -> Settings -> Permalinks or use flush_rewrite_fules() (“important note”)


Option #1

I used regions as url prefix and region_cpt as query variable name, you can change them to the ones you prefer.

add_action( 'init', 'se333633_rewrite_tags', 10 );
add_action( 'init', 'se333633_rewrite_rules' );
add_action( 'pre_get_posts', 'se333633_pre_get_posts', 30 );

function se333633_rewrite_tags() 
{
     add_rewrite_tag( '%region_cpt%', '([^/]+)' );
}

function se333633_rewrite_rules()
{
     add_rewrite_rule(
         '^regions/([^/]+)/([^/]+)/?$',
         'index.php?taxonomy=region&region=$matches[1]&region_cpt=$matches[2]',
         'top'
     );
}

function se333633_pre_get_posts( $query ) 
{
     $cpt_for_region = get_query_var( 'region_cpt', false );
     if ($cpt_from_region !== false && $query->is_main_query() ) 
     {
         $query->query_vars['post_type'] = $cpt_for_region;
     }
}

Option #2

In the code from option #1, se333633_rewrite_rules changes and two new functions are added to refresh rewrite rules after changes in terms.

add_action( 'created_term', 'se333633_created_term', 20, 3);
add_action( 'delete_region', 'se333633_region_term_deleted' );

function se333633_rewrite_rules()
{
     $region_terms = get_terms([
         'taxonomy'  => 'region',
         'hide_empty' => false,
         'fields'    => 'id=>slug',
     ]);
     if ( is_array($region_terms) && count($region_terms) )
     {
         foreach( $region_terms as $slug )
         {
             add_rewrite_rule(
                 '^'.$slug.'/([^/]+)/?$',
                 'index.php?taxonomy=region&region='. $slug. '&region_cpt=$matches[1]',
                 'top'
             );
         }
     }
}
function se333633_created_term ( $term_id, $tt_id, $taxonomy ) 
{
     if ( $taxonomy != 'region')
         return;
     flush_rewrite_rules();
}
function se333633_region_term_deleted( $term )
{
     flush_rewrite_rules();
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment