Many to Many Taxonomies or rewrite rules?

Im developing a theme for article directory and i would like to link some way taxonomies to taxonomies.
I had a custom post type called “company” with 2 taxnomies

Taxonomy #1 is category with terms(shops,workshops,churchs,shoppings,etc)

Taxonomy #2 is location with terms(New York, New jersey,Miami, etc)

With taxonomy #1 i can do

Read More
local.dev/category/shops/ to list all shops

With taxonomy #2 i can do

local.dev/location/miami/ to list all companies in miami

Currently i got working by this answer of MikeSchinkel the following:

 local.dev/shops/

Which i can use also like these to retrieve my desired results:

local.dev/shops/?location=miami

BUT i would like to do

local.dev/shops/miami or 
local.dev/shops/new-york to list shops in different locations 

Making all the urls looks nicer. Is this possible? My terms will be all unique so im sure they will not collide with posts tags or slugs or anything

Probably the best thing is to create a rewrite rule right? Please give me some light on this, im a bit lost.

Related posts

Leave a Reply

3 comments

  1. With reservations, sure, you can do what you want with this code:

    add_action('init', 'wpse_61376_rewrites');
    function wpse_61376_rewrites() {
        add_rewrite_rule('^([^/]*)/([^/]*)/?$','index.php?category=$matches[1]&location=$matches[2]','top');
    }
    

    Then go to Settings → Permalinks in wp-admin and click “Save Changes” to flush your rewrites and activate this rule.

    Reservations

    The problem about not having a prefix is that any permalink with two “sections” (site.com/section-1/section-2/) will be caught by this permalink and you will end up with undesirable behavior at times. For instance, an entry with two pages, site.com/about/2 or a comments feed for a page, site.com/page/feed/. Long story short, this is ill-advised.

    Suggested Solution

    If it were me, I would add a static base for safety. You said this was for a directory, so that seems a good base:

    add_action('init', 'wpse_61376_rewrites');
    function wpse_61376_rewrites() {
        add_rewrite_rule('^directory/([^/]*)/([^/]*)/?$','index.php?category=$matches[1]&location=$matches[2]','top');
    }
    

    Your URLs would then be, site.com/directory/shops/miami/, which is as intuitive as it is safe.

  2. Since WordPress 3.1 you can query by multiple taxonomy in the URL out of the box, without any development. This only works with query strings be default, and custom rewrite rules will need to be added to make this work in a pretty permalink fashion.

    Out of the box

    local.dev/company/?category=shops&location=new-york

    This works great, and will grab any post of type ‘company’ that has the shops category and the new-york location. Obviously though, it looks like you want this to function with pretty permalinks, so we’ll need to create some custom rewrite rules.

    Pretty Permalinks

    First thing we need to do is make clear what the pattern we want for this multiple taxonomy permalink structure. In your question you used /shop/new-york. This could work, but there are many problems with it.

    Taxonomy Base

    It is important to understand that all urls to the WordPress front end get “rewritten” and end up as requests to index.php. Not the index.php in your theme, rather the one in the core application. Things like post slugs, category names, tags, etc – they all get sent that GET query vars. The rewrite rules are regex patterns that turn /foo/bar into index.php?foo=bar or whatever the query string is that needs to be built.

    Because your examples have no “taxonomy base”, /shops will collide with any post of any post type that shares the slug. Additionally, since we are not using any category base at all, it would be complicated to for the rewrite rules to know when a certain URL is /<cat-term>/<location-term> or supposed to be /<cat-term>/<post-slug>. Think of taxonomy bases as name spacing – if we instead used /category/shops/location/new-york, then the regex could be written to look for a pattern of /category/<cat-term>/location/<location-term>, and do so unambiguously.

    With that said, even though you asked for /<cat-term>/<location-term>, I am going to go through this explanation using /category/<cat-term>/location/<location-term>.

    Post Type Base

    As with the taxonomy base my examples assume you have company set as the slug for your “company” custom post type – and is certainly optional, you can chose not to use one. Simply be aware that if that post type base is not present then the query will not filter by post type company, and the resulting archive will show any posts of any post type that meet the taxonomy criteria.

    Rewrite Rules

    There are a few options as to how this can work, to varying degrees of flexibility depending on what you want. I’m going to create two extremes, one where everything is rigid, and one that is completely flexible. You’ll have to comment one out otherwise they overwrite each other if you just run it as is. Pick the one you like best, or modify them to meet somewhere in the middle.

    function multiple_taxonomy_rewrite_rules( $rules ){
    
        /**
         * This is very strict. Only allows category first, 
         * and location must be second. 
         *
         * This is also an example of how to make this rule 
         * only work for the "company" post type. 
         * Notice the hard-coded "post_type=company" string
         * at the end.
         *
         * This will only work with the following structure...
         * * /company/category/<term>/location/<term>
         */
        $newrules['company/category/(.+)/location/(.+)/?$'] = 'index.php?=category=$matches[1]&location=$matches[2]&post_type=company';
    
        /**
         * This is a very flexible version. Allowing for 
         * almost any permutation of the structure. 
         * 
         * This will work with...
         * * /<any-post-type>/category/<term>/location/<term>
         * * /<any-post-type>/location/<term>/category/<term>
         * * /category/<term>/location/<term> (no post type base - meaning query doesnt filter by post type)
         * * /location/<term>/category/<term> (no post type base - meaning query doesnt filter by post type)
         */
        $newrules['(.+)?/?(category|location)/(.+)/(category|location)/(.+)/?$'] = 'index.php?=post_type=$matches[1]&$matches[2]=$matches[3]&$matches[4]=$matches[5]';
    
        return $newrules + $rules;
    }
    add_action('rewrite_rules_array', 'multiple_taxonomy_rewrite_rules');
    

    Hopefully this is helpful.

    EDIT:

    To clarify, the issues regarding taxonomy or post type bases are not hard technical limitations, they are words of caution. Less unique patterns are ok to varying degrees, but the less unique, the more chance there is of a collision. You should simply be aware of the consequences of any particular pattern are.

    I explained in the comments that it would also be possible to use a unique slug as a prefix for this particular pattern. You could use…

    'company/(.+)/(.+)/?$' = 'index.php?category=$matches[1]&location=$matches[2]&post_type=company';
    

    Keep in mind, that in this scenario I am only using example taxonomies so those could just as easily be replaced with category and region, or any pair of taxonomies you like. – also, I am using the prefix company, but it doesn’t matter what it is, as long as its a unique string.

  3. Basics

    Just use get_terms() in combination with get_site_url().

    Multisite

    To add MU support, just drop in the get_current_blog_id() to retrieve the ID of the current blog. This gives you the $blog_id, which is the only blog in a single site setup and therefore works too for non-MU installations.

    Example

    Now, let’s build a list.

    echo '<ul>';
    foreach ( get_terms( "shops" ) ) as $shop )
    {
        $site_url = get_site_url( get_current_blog_id() );
        echo "<li><a href='{$site_url}shops/{$shop->slug}' title='{$shop->description}'>{$shop->name}</a></li>";
    }
    echo '</ul>';
    

    This example assumes, that you’ve just your site url and your taxonomy terms slug appended.

    http://example.com/shops/miami