WordPress 3.1 introduced the best new feature I failed to notice – built-in support for filtering posts by multiple taxonomies.
For a post index or custom post type archive, instead of being constrained to queries for one taxonomy like this:
www.example.com/post-type/taxonomy_x/term_x/
We can do this:
www.example.com/post-type/?taxonomy_y=term_y&taxonomy_x=term_x
WordPress will automatically filter the posts to include only those posts tagged with term_y in taxonomy_y and term_x in taxonomy_x.
What is it Good For?
Imagine you have an Event post type with both a Location and Industry taxonomy. You want to give site visitors a way to filter events to show only events in certain locations & relevant to certain industries. For example, a URL to show only events in Sydney for the Web Development industry.
This is exactly the problem I was given while working with_FindingSimple on the Australian Government’s new EEX site.
Previously, we would have to do all sorts of manual SQL madness to get this type of thing to work, but now WordPress can do it all for us… if we ask nicely.
Taxonomy Queries
The Codex has a good article that introduces taxonomy queries. Otto has also published a great tutorial to cover the fundamentals of running advanced taxonomy queries.
Both of these articles use the query_posts
function to create a taxonomy query. They stop short of showing how it’s done with URLs.
It’s crazy simple with URLs, as with the example I opened with, just add the taxonomy as a parameter and the term/s as the value of that parameter.
Returning to the events example, this URL will filter events to show only those in New South Wales and of relevance to the web industry.
www.example.com/events/?location=nsw&industry=web
Voilà, WordPress will filter the events automatically.
Now you may think – my visitors can manually enter unknown search parameters to filter posts, whoop-dee-doo! – we’ll get to a solution for this soon. For now, let’s take it a little further with the taxonomy filters.
Let’s say we want to show events in New South Wales OR Queensland, which are related to the web AND mobile industries. We can use the following URL:
www.example.com/events/?location=nsw,qld&industry=web+mobile
Simple as that. WordPress will now filter events for two locations using an OR operator and for two industries using an AND operator.
All we had to do was use the plus (+) symbol to tell WordPress to select events tagged with both industries and a comma (,) to tell WordPress to select events in either location.
Pretty URL Taxonomy Queries
Now let’s take it up a notch.
One of WordPress’s best features is its pretty permalinks. Instead of URLs like:
http://example.com?page=123
WordPress can use nice URLs like:
http://example.com/page-name/
Search engines love it. Humans love it.
So how can we create a pretty permalink which WordPress will translate into an advanced taxonomy query like those above?
Something like:
www.example.com/events/location/nsw,qld/industry/web+mobile/
It’s a 2 step process:
- Tell WordPress about the URL structure, and how it should handle requests with that URL structure, that is, give WordPress the rewrite rules;
- Generate the pretty URLs.
Step 1: The Rewrite Rules
We need to add rewrite rules to tell WordPress what to do with our pretty URLs.
A rewrite rule is a key => value pair where the key is the pretty URL to match against and the value is the URL WordPress can process. To set a rule, use an associative array and add it to the global $wp_rewrites
object.
The best hook for adding custom rewrite rules is the 'generate_rewrite_rules'
hook. For example:
function eg_add_rewrite_rules() { global $wp_rewrite; $new_rules = array( 'event/industry/(.+)/?$' => 'index.php?post_type=eg_event&industry=' . $wp_rewrite->preg_index(1) ); $wp_rewrite->rules = $new_rules + $wp_rewrite->rules; } add_action( 'generate_rewrite_rules', 'eg_add_rewrite_rules' );
A few things to pay special attention to in this example:
- The new rules get prepended to the existing rewrite rules – this is very important, if the code was
$wp_rewrite->rules + $new_rules
instead of$new_rules + $wp_rewrite->rules
WordPress’s default rewrites would capture the URL before our rules even got a look-in. - The function uses the
$wp_rewrite
back reference as represented by$wp_rewrite->preg_index(1)
instead of using a vanilla regex back reference, like$1
- This function can only be run after the
$wp_rewrite
has been set up in order for$wp_rewrite->preg_index(1)
to exist.
Once you have added the rules and hooked to generate_rewrite_rules
, you must visit the Permalink admin page to have WordPress call the generate_rewrite_rules
hook. In development, if you are regularly changing the rules, you can also use the flush_rewrite_rules()
function. This is a very resource intensive function though, so best not to use it in live code unless it’s only called once on plugin or theme activation.
Step 1.1: Rewrite Rules Powerset
Now for the fun part.
The example above hardcodes the industry taxonomy. This is OK for one taxonomy, but we’ve got at least two (industry & location), and we may add more in the future. What we need then is some rewrite rules to match every possible combination and permutation of taxonomies for the event post type.
You may recognise this as a set theory problem. Each taxonomy with its terms represents a set, we want the power set of all taxonomies. There are a few PHP classes around the place to compute power sets. I tried one of these, but it created 24 rules for the 4 taxonomies I was using. Worse still, this verbosity would expand dramatically with each new taxonomy. The number of rules is the factorial of the number of taxonomies – 4! = 24, 5! = 120. I wanted a more concise method.
After chatting with dd32 about the regex available in rewrites rules, I realised there was a much more concise method which would add just one line per taxonomy. The regex for the rewrites can match anything, so why not match the taxonomy and the taxonomy term.
Returning to the location & industry example, we get two rules that look like this:
function eg_add_rewrite_rules() { global $wp_rewrite; $new_rules = array( 'event/(industry|location)/(.+?)/(industry|location)/(.+?)/?$' => 'index.php?post_type=eg_event&' . $wp_rewrite->preg_index(1) . '=' . $wp_rewrite->preg_index(2) . '&' . $wp_rewrite->preg_index(3) . '=' . $wp_rewrite->preg_index(4), 'event/(industry|location)/(.+)/?$' => 'index.php?post_type=eg_event&' . $wp_rewrite->preg_index(1) . '=' . $wp_rewrite->preg_index(2) ); $wp_rewrite->rules = $new_rules + $wp_rewrite->rules; } add_action( 'generate_rewrite_rules', 'eg_add_rewrite_rules' );
See what’s happening there? We’re matching either industry or location as a taxonomy.
Let’s take a look at how these rewrite rules would apply to the following URL.
www.example.com/events/location/nsw/industry/web/
In the first instance, our regex would capture the following:
$wp_rewrite->preg_index(1)
would capture the taxonomy location$wp_rewrite->preg_index(2)
would capture the location taxonomy term nsw$wp_rewrite->preg_index(3)
would capture the taxonomy industry$wp_rewrite->preg_index(4)
would capture the industry taxonomy term web
The URL it would pass to WordPress for processing would then be:
www.example.com/events/?location=nsw&industry=web
Which is the URL we saw in the opening example.
Step 2: Creating the Permalink
Earlier I mentioned how useless it is having a URL structure that requires the user to know parameters and their possible values. Let’s now create a function to generate permalinks with appropriate filters parameters. We could use this function in a widget or hardcoded sidebar on our post type archive page.
The function needs to:
- Maintain taxonomy term filters for the current page request;
- Add taxonomy terms to a permalink, but only when they are not already set in the URL;
- Remove terms when they are already set in the URL; and
- Combine terms if a given term is within a taxonomy that is already being used to filter.
Taking these criteria into account, we get something like the eg_get_filter_permalink
function below.
This function takes a $taxonomy_slug
slug and a taxonomy $term
to filter by. It combines terms of the same taxonomy with a plus (+), so WordPress will use an AND operator to combine the terms. You could just as easily set this to a comma (,) for WordPress to combine terms with an OR operator.
I’ll let the code do the rest of the talking.
function eg_get_filter_permalink( $taxonomy_slug, $term ) { global $wp_query; // If there is already a filter running for this taxonomy if( isset( $wp_query->query_vars[$taxonomy_slug] ) ){ // And the term for this URL is not already being used to filter the taxonomy if( strpos( $wp_query->query_vars[$taxonomy_slug], $term ) === false ) { // Append the term $filter_query = $taxonomy_slug . '/' . $wp_query->query_vars[$taxonomy_slug] . '+' . $term; } else { // Otherwise, remove the term if( $wp_query->query_vars[$taxonomy_slug] == $term ) { $filter_query = ''; } else { $filter = str_replace( $term, '', $wp_query->query_vars[$taxonomy_slug] ); // Remove any residual + symbols left behind $filter = str_replace( '++', '+', $filter ); $filter = preg_replace( '/(^\+|\+$)/', '', $filter ); $filter_query = $taxonomy_slug . '/' . $filter; } } } else { $filter_query = $taxonomy_slug . '/' . $term; } // Maintain the filters for other taxonomies if( isset( $wp_query->tax_query ) ) { foreach( $wp_query->tax_query->queries as $query ) { $tax = get_taxonomy( $query['taxonomy'] ); // Have we already handled this taxonomy? if( $tax->query_var == $taxonomy_slug ) continue; // Make sure taxonomy hasn't already been added to query string if( strpos( $existing_query, $tax->query_var ) === false ) $existing_query .= $tax->query_var . '/' . $wp_query->query_vars[$tax->query_var] . '/'; } } if( isset( $existing_query ) ) $filter_query = $existing_query . $filter_query; return trailingslashit( get_post_type_archive_link( 'eg_event' ) . $filter_query ); }
Now to create a pretty permalink for our filter, we can use this function something like this:
// Link to page with only events in NSW echo eg_get_filter_permalink( 'location', 'nsw' );
Even better, we can loop over all taxonomies and their terms to output every possible filter link.
Bonus Prize: A Rewrite Rule Generator
As you’ve read this far, I think you deserve a reward.
It’s quite likely you will at some stage want to programmatically generate rewrite rules for multiple custom post types with various taxonomies. You could do it manually, but that’s no fun. Let’s use an automatic rewrite rule generator.
The function below takes a post type as the first parameter and creates a series of rewrite rules for each of its taxonomies. The function also includes an optional second parameter, $query_vars,
which is an array of any non-taxonomy query variables you may want to add rewrite rules for. It’s ideal for adding query vars for advanced metadata queries. For example, our event post type could use a date query variable.
/** * Generates all the rewrite rules for a given post type. * * The rewrite rules allow a post type to be filtered by all possible combinations & permutations * of taxonomies that apply to the specified post type and additional query_vars specified with * the $query_vars parameter. * * Must be called from a function hooked to the 'generate_rewrite_rules' action so that the global * $wp_rewrite->preg_index function returns the correct value. * * @param string|object $post_type The post type for which you wish to create the rewrite rules * @param array $query_vars optional Non-taxonomy query vars you wish to create rewrite rules for. Rules will be created to capture any single string for the query_var, that is, a rule of the form '/query_var/(.+)/' * * @author Brent Shepherd <me@brentshepherd.com> * @since 1.0 */ function eg_generate_rewrite_rules( $post_type, $query_vars = array() ) { global $wp_rewrite; if( ! is_object( $post_type ) ) $post_type = get_post_type_object( $post_type ); $new_rewrite_rules = array(); $taxonomies = get_object_taxonomies( $post_type->name, 'objects' ); // Add taxonomy filters to the query vars array foreach( $taxonomies as $taxonomy ) $query_vars[] = $taxonomy->query_var; // Loop over all the possible combinations of the query vars for( $i = 1; $i <= count( $query_vars ); $i++ ) { $new_rewrite_rule = $post_type->rewrite['slug'] . '/'; $new_query_string = 'index.php?post_type=' . $post_type->name; // Prepend the rewrites & queries for( $n = 1; $n <= $i; $n++ ) { $new_rewrite_rule .= '(' . implode( '|', $query_vars ) . ')/(.+?)/'; $new_query_string .= '&' . $wp_rewrite->preg_index( $n * 2 - 1 ) . '=' . $wp_rewrite->preg_index( $n * 2 ); } // Allow paging of filtered post type - WordPress expects 'page' in the URL but uses 'paged' in the query string so paging doesn't fit into our regex $new_paged_rewrite_rule = $new_rewrite_rule . 'page/([0-9]{1,})/'; $new_paged_query_string = $new_query_string . '&paged=' . $wp_rewrite->preg_index( $i * 2 + 1 ); // Make the trailing backslash optional $new_paged_rewrite_rule = $new_paged_rewrite_rule . '?$'; $new_rewrite_rule = $new_rewrite_rule . '?$'; // Add the new rewrites $new_rewrite_rules = array( $new_paged_rewrite_rule => $new_paged_query_string, $new_rewrite_rule => $new_query_string ) + $new_rewrite_rules; } return $new_rewrite_rules; }
And there we have it. A full arsenal to generate and accept pretty URLs for advanced taxonomy queries. Not as simple as appending ?taxonomy=term
parameters to a URL, but more user and search engine friendly.
Update:
To help you debug your rewrite rules, download and install Monkeyman’s Rewrite Analyzer. It’s simply excellent.
Great post Brent!
I had used the multi taxonomy queries in WordPress, but had never taken the time to implement custom rewrite rules for them.
James
Hey James, as the post demonstrates, there is a big gap between the time it takes to implement multi-tax queries with GET parameters compared to pretty URLs, so 9 times out of 10 the GET parameter approach would be the only feasible option. For the project I was working on, pretty URLs were the best option though so it was worth the extra time.
I hope this post helps others get there quicker now though!
Awesome article. I’ve been looking and looking for a good example of multiple query vars and rewrite rules. I wish your post existed a month ago!
Oh, I think you have a syntax error in eg_generate_rewrite_rules.
Thanks Joey, glad it helped. I’ve fixed the error in the
eg_generate_rewrite_rules
function too – copy & paste fail!Thank you for this awesome tip !
Awesome to improve wordpress performances.
I wrote a small plugin to make it dynamic for your taxonomy, if you want more informations you have my e-mail I guess
Hi Brent,
I been trying to filter specific custom post type by taxonomies, where the taxonomy is also shared by different custom post types. I used some plugin to sort the taxonomy, but it is directing to taxonomy archive instead of sorting within the specific custom post type archive.
I been trying to do /url/hotelreviews/location=singapore where hotelreviews is post type and location is taxonomy. but it keep directing to /url/location=singapore. Since it is taxonomy archive, it get all custom post type of the same tax=location=singapore all listed (like hotel reivews and restaurant reviews type all listed). but i want only filter by location=singapore in specific post type hotel reviews.
Any advice that your tutorial on rewrite can help?
Hi Wan, make sure you specify the post type in your rewrite rules.
Your code will need to look something like this:
Pingback: How WordPress Taxonomy Queries Could Be More Awesomer | MaisonBisson.com
Hi Brent,
Your code about eg_generate_rewrite_rules still contains an error. I believe some parts are missing. It would be great if you could fix it up right again 😉
Many thanks!
Right you are Jesse – fixed now.
Hey Brent … first, thanks for presenting this code… we’ve been able to use the ideas on a few sites recently.
We’ve encountered some issues though (most recently on a sub-blog on a multisite install) – in case anyone else encounters similar:
1. we found if the template taxonomy.php was present, WordPress was using that rather than our archive-cpt.php to display the posts. It was getting passed the correct posts to show based on the url (cpt, taxonomy 1, taxonomy 2 parameters), but was choosing taxonomy.php as the template to use. Remove the taxonomy.php template and everything works fine.
2. we had some problems hooking on generate_rewrite_rules (instead we had to hook on init to get them picked up on – in some cases)
3. we found modifying the rewrite array directly caused problems (WSOD) – but, instead using the add_rewrite_rule() function call worked without problem. I suspect this is probably the safest option anyway. Note: we only had this problem on our live environment; on our dev environment, both worked.
Cheers, Roger
Hi. Excelent tutorial. But I have a question regarding this url
http://www.example.com/events/location/nsw,qld/industry/web+mobile/ What would be themplate wordpress would try to load?
Take a look at the template hierarchy: http://codex.wordpress.org/Template_Hierarchy
For me it loads the custom post type archive template, but based on what Roger said above, WP may try to load the
taxonomy.php
file first.What will happen, if I have hierarchical taxonomy like categories? for example
Add a Country/City style and add a City term to post. How would wordpress manage that then?
I don’t think it will change anything, but why not try it yourself and see?
Great Post!
But I found one mistake:
at $new_rules = array(rule1, rule2) there is missing a comma between the 2 rules.
And perhaps you could mention, that in the Dashboard the Permalink-Structure has to be saved once (without changing anything) to get the code working?
Thanks for pointing that out. I’ve also added instructions for making WordPress flush the rewrite rules when adding new rules.
Hi, excelent tutorial.
What do you think, wolud it be possible to extend your permalink function with a kind of
remove_query_arg(), add_query_arg() functionality?
Cheers, Attila
Hi Attila, I can definitely see how that would work. I did have individual functions for that, but not a central super function. If there are hooks in
add_query_arg()
you wouldn’t even need a whole new function.i had a hell of a time actually getting the rewrite generator launched, so for anyone that is stuck with that, here is the function to add:
i love this function, but i’m looking in to how to make it work with the rewrite slugs of the taxonomies versus their query vars. i’m a little confused by preg_index… if we redefine the $query_vars variable to hold the rewrite slugs, then i’m not sure where to “translate” those slugs into their proper query vars
for instance, with your code my URL works with
product_cat/fabrics/pa_color/red
but, eventually, i need to it be:
product-category/fabrics/color/red
Hi Kathy, if you want the query var to be product-category instead of product_cat, you don’t need to mess with the rewrites but rather go straight to the source and set the
query_var
value in theregister_taxonomy()
function call (function reference in the Codex).For example:
I’m new to the whole add_action / hooks thing, and your comment helped me figure out how and where to use Brent’s powerful function. Thanks 😀
Reblogged this on Reinaldo Ferroe comentado:
Artigo mais que interessante 🙂
Tks i.am
This is great, and worked perfectly for my custom taxonomies and post types, thank you! Any thoughts about adding it to a post type of post and the delivered category and tag taxonomies? So a permalink might be site.com/category/cat1/tag/tag1 or another might be site.com/category/cat1/customtax/tax1. Thanks again!
Also, sorry to comment again – used Kathy’s function to add the rewrite rules and it’s working swimmingly for one post type – how would i add it to multiple post types? sorry if that’s a newbie php question! thank you!
You need to call
eg_generate_rewrite_rules( $product_type )
for each of the post types.Something like the following should work (but is untested):
Where
$post_types
is an array of all the post types you want to create rewrite rules for.thanks so much! worked like a charm. and i see in the code why this doesn’t work for the default post type of post (it wants that permalink). i’ll have to play to see if i can get it to work. thank you for your help!
Even better, we can loop over all taxonomies and their terms to output every possible filter link. 🙂 How is that 🙂
This code do it
Classifications
$taxonomy,
'orderby' => $orderby,
'pad_counts' => $pad_counts,
'hierarchical' => $hierarchical,
'title_li' => $title,
'hide_empty' => $empty,
'echo' => $echo
);
?>
category_nicename.'' ) ."";
$option = 'cat_name.'">';
$option .= $classification->cat_name;
$option .= ' ('.$classification->category_count.')';
$option .= '';
echo $option;
}
?>
This is very nice article
helps me a lot
thanks
Hi,
i have a question at step 1. Where do i have to register this function. I mean in wich file?
Best regards
Hi Phil, you can use it in any file that is loaded with the loading of WordPress.
Pingback: If you want a way to generate URL’s… « Random Thoughts
Thanks you soo much
i was doing this in an ugly way
now i am really happy with my client
🙂
Do you know where I can find information on just passing variables and not a taxonomy? Essentially I’m querying a database with a whole bunch of variables. Which are not posts. Making them posts would add about 20000 posts to my database. I already have all my queries written but I just want a pretty url.
http://www.mysite.com/MyPage/?apples=1&pears=2&peaches=3
into
http://www.mysite.com/MyPage/apples/1/pears/2/peaches/3
I’ve been searching for my answer for over 1 year and never found it.
Thanks!
Danielle, what you need are custom rewrite rules for your variables.
Something like:
And add
apples
andpears
as custom query variables:And then a way to create the actual links for your custom variables used to hyperlink content.
It’s really a lot more complicated than that, and you’ll need to learn a lot so I’d recommend you head to Google to find tutorials on “Custom Rewrite Rules”, like this one for Tutsplus
I have got the same problem:
For example, i need: http://www.example.org/location/canada/category/dogs
Then i have wrote this code:
function eg_add_query_vars( $query_vars ) {
$new_vars = array( ‘location’, ‘category’ );
return array_merge( $new_vars, $query_vars );
}
add_filter( ‘query_vars’, ‘eg_add_query_vars’ );
function eg_add_rewrite_rules() {
global $wp_rewrite;
$new_rules = array(
‘en/(.+)/de/(.+)/?$’ => ‘index.php?location=’ . $wp_rewrite->preg_index(1) . ‘&category_name=’ . $wp_rewrite->preg_index(2)
);
$wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action( ‘generate_rewrite_rules’, ‘eg_add_rewrite_rules’ );
It does not work.
Sorry this is the correct code:
The regex match I provided might be too greedy on second look, try:
To help you debug, checkout Monkeyman Rewrite Analyzer
Thanks a lot. It worked! finally after so much time!
thank you very match
that’s good info,
I am having problems getting the basic URL query to work as when I put the equivalent of http://www.example.com/post-type/?taxonomy_y=term_y&taxonomy_x=term_x in, my installation of WordPress returns the same page it would had I not put in the variables, so the equivalent of http://www.example.com/post-type/
Any suggestions of what might be causing this? (I have deactivated all plugins and still no luck)
Thanks,Matt
Hrm, interesting. Which version of WordPress are you using?
Also, can you share the code used to register the post type & taxonomies? (between <pre></pre> tags in the comment or just link to a Gist).
Great post, thanks for this. It doesn’t take nicely to taxonomies where the ‘rewrite’ argument is supplied. I haven’t had time to look at that, but otherwise this works great.
Hi, can you please tell me what is wrong with my code below.
network-camera, camera series and camera brands are three taxonomies i created. what i want is to change the url
http://www.example.com/?camera-type=xx&camera-series=xx&camera-brand=xx
to
http://www.example.com/camera-type/xx/camera-series/xx/camera-brand/xx
please help me with this.
Thanks,
What are you seeing to make you think this isn’t working?
From a brief glance, the code looks OK (except that in your example URL you refer to a “camera-type” rewrite var which isn’t anywhere in your code). You might also want to make the regex less greedy – see this comment for details.
Also, to help you debug rewrites, checkout Monkeyman Rewrite Analyzer.
Thanks for replying,
yes the example url i mentioned was wrong sorry about that.
i installed the analyzer it is showing the rule i created on the top but when i search the url format
network-camera/xx/camera-series/xx/camera-brand/xx
it is triggering the taxonomy rule which was predefined “network-camera/(.+?)/?$”
is there a way to override this issue.
thanks,
That could mean a few things, most likely that:
So you’ll have to experiment with your regex and the regex rule for the other taxonomy until Monkeyman gives you what you want. 🙂
Thanks a million for such an awesome explanation! I’m using it now for a custom query with two taxonomies (Woocommerce attributes).
Also, I’d appreciate it if you could point me in the right direction with an URL permalink structure. I need the URL for my custom post type (product) to look like:
Site.com/taxonomy1_term/taxonomy2_term/postname/
Do you think that’s doable?
Thank you
Hi marcefx, doing a permalink structure without prefixes gets difficult. If you look at how WordPress interprets the rewrites, it needs the prefix to know what object type you are querying.
I have done something like this before though, and the way I decided to do it was to create rewrite rules with all taxonomy terms explicitly defined.
For example, for a taxonomy
eg_colour
with the terms red, white, blue and a taxonomyeg_size
with the terms small, medium and large, you’d need rewrites like this:Naturally, you’d query the taxonomy and dynamically add the terms so it’s not hardcoded, but I’ve hardcoded it hear to show the result you need to end up with.
There are all sorts of issues and further considerations when doing this too, for example:
Good luck!
Thanks, Brent 🙂 I guess you’re right. I was able to rewrite the URL, but it’s causing really weird issues. My pagination is not working, and the blog posts went 404.
So, I decided to use regular categories and the /%category%/%postname%/ structure. I’m going to try to use the same term for category1 and taxonomy1. I will add a “Noindex” rel to all categories and see what happens…
Cheers!
Hello,
Great article! Thanks a lot.. will definitely use this!
Is it possible to specify a pretty URL for taxonomy where it doesn’t matter what term within that taxonomy is used as long as a post is associated with that taxonomy. I hope that makes sense.
I’ve tried couple of rules:
1. add_rewrite_rule( ‘^shop/labels/?$’, ‘index.php?post_type=product&taxonomy=label’, ‘top’ );
2. add_rewrite_rule( ‘^shop/labels/?$’, ‘index.php?post_type=product&label’, ‘top’ );
However, none of them are working. It works fine if I specify an exact term for the taxonomy. However, there are a lot of terms for that taxonomy… the only thing I’d like to do is to get all the products that have ‘label’ taxonomy, no matter what exact term from ‘label’ taxonomy is being used.
Any ideas how I can get that? I would hugely appreciate any tips
Thanks,
Dasha
Hi Dasha,
I’m not sure. Your first rewrite looks correct for achieving something like that, but I’m not sure if
WP_Query
will handle it correctly.The only way to *really* find out would be to go through the core code in
WP_Query
and see how it builds the CPT archive query (i.e. when the ‘post_type’ query arg is set) and see if there is also a way to have a sort of “has_tax” argument instead of a “taxonomy” equals argument.Brent
Hi Brent,
Thanks for your reply. I’ve looked at the code of WP_Query class more and couldn’t figure it out 😦 doh… I think it might be impossible to do with WP as it is. Would be great to know how to do such thing… will look again in a bit when I have more time (probably couple of weeks sadly).
If anyone knows or find out, let me know 🙂
Thanks! Dasha
Hi Brent, amazing post and thanks very much. I have a question, if you are able to help us. If this is too complex for a quick answer are you available for hire?
We have a WordPress site for reviewing local businesses. We are using the plugin WP No Taxonomy Base … in order to keep URLs cleaner.
Therefore we have as follows:
– example.com/restaurants/ … (instead of example.com/type/restaurants/)
– example.com/chicago/ … (instead of example.com/region/chicago/)
We are hoping to support these pretty URLs as follows:
– example.com/restaurants/chicago/
– example.com/hotels/chicago/
The taxonomies are called “type” and “region” respectively, and the custom post type is called “property” posts. Can you tell us how to generate these pretty URLs properly, without the taxonomy bases showing up?
I will keep checking back here to see if you reply. Thank you for the very informative piece and for helping out the WordPress community.
Hi Jared, creating permalinks without a taxonomy prefix makes things difficult. I’ve touched on an answer in this comment. I’m not available for hire to implement it, but if you point a WordPress developer to this post and that comment, that should get them started. 🙂
Pingback: Tweet Parade (no.44 Oct-Nov 2012) | gonzoblog
This post is excellent and exactly what I am trying to accomplish. I am not a developer but can read code somewhat and get my way around wordpress. I have a couple questions.
I have a current post type dealer. I have a brands and state taxonomy.
My current permalink looks like this xyz.com/dealers/brands/honda/ and xyz.com/dealers/brands/toyota/
I am trying to rewrite; xyz.com/dealers/brands/honda/?state=texas and xyz.com/dealers/brands/toyota/?state=texas
Can you tell me how to modify your step 1 code and Step 2 code for my situation above?
Secondly is it ok if I place these codes in my themes functions.php file?
Lastly, you mention “Once you have added the rules and hooked to generate_rewrite_rules, you must visit the Permalink admin page to have WordPress call the generate_rewrite_rules hook.”
Can you explain exactly what step I need to take in order to accomplish this?
Thank you again for the great post!!!!
Hi Evan,
All the information you need to get to what you want is available in this post and the WordPress Codex. It might just take you longer to get there without being an experienced WordPress programmer.
If I tell you how to modify it, you won’t learn anything and I’d be giving your free custom web development (and if I did that for everyone who asked, I’d be broke and completely without sleep).
Yep that’s fine.
Visit the Permalink admin page.
Ok, thank you I understand. Is there anyway you can tell me what the
?post_type=eg_event means? That was the main part I was confused on. I see you have a custom post type named events, but how does that relate to eg_event in your rewrite. Thanks!
The
eg_event
is the name of the post type, that is, the value used in the$post_type
parameter in the register_post_type() call.So you have a post type dealer, including
post_type=dealer
would filter posts to show only those which are the dealer type (assuming that dealer is the value you used for the$post_type
parameter in your register_post_type() call).Hope that helps! 🙂
It has taken a while, but I finally have the working rewrite I was looking for based on your idea. I did learn a lot in the process, thank you for not giving me the answer! However, there is just one part of the code I am just not certain what is doing. It involves the $wp_rewrite->preg_index(1) or $wp_rewrite->preg_index(2). Can you give me a brief understanding of what these are doing. Thanks!
It is returning a matches from the regular expression.
It will probably take a bit of an understanding of regex to understand what that means. Reading up on the preg_match function should help.
Basically, the first part of a rewrite rule can match parts of the URL by including two parentheses
()
. Thepreg_index()
function is can then be used to access those matched pieces.Pingback: WP_Query详解(译自Wordpress官方资料) _ 蓝色妖姬
Hey great post. However i have a rather specific custom solutuion i require and was wondering whether you could guide me in the right direction. I have a site that has a custom post type list / archive that is filtered by a custom post type taxonomy and follows the below url structure http://www.example.name/page-one/page-two/events/cat/taxonomy-term. Where page-one is a standard wordpress post = page, page-two is a standard wordpress post = page, events is a standard wordpresds post = page. The events page has a custom template attached that filters the custom post type by taxonomy term passed in the URL. I have this working without the Pretty URL in the format http://www.example.name/?p=123&cat=term-slug. Im having trouble translatng this into the correct re-write rule. See my below code – it directs me to the right page but doesn’t append the /cat/taxonomy-term to the URL so i cant grab the end term to use as a filter in my custom query. Is there anything im missing
i have the rewrite analyzer installed – its top and catches the pagename and cat var. Pls help!!!
The rewrite code looks OK from first glance. When you say:
Do you mean the
cat
value is not being set in the$wp_query
params?You shouldn’t have to do a custom query if the rewrites are working.
Paste the code you’re using in your custom query and explain which file/hook it is being run in.
OK – heres the first part that is used for the category filter – it highlights as selected when returned to the page
The second part runs the custom query that populates the archive list
You shouldn’t have to populate the query yourself. The whole idea of creating custom rewrites is to get WordPress to do it for you.
Interesting stuff. Do you know if it’s possible, instead of saying ‘if this post contains a or b in taxonomy x’, to say, ‘if this post contains a in taxonomy x OR a in taxonomy y’ in a URL? (So, for example, if I want to search both article text and tags for the word ‘puppy’, in one URL?)
It’s possible to run that kind of query. The Taxonomy Parameters section of the
WP_Query
codex article has an example at the end which selects posts that are in the quotes category OR have the quote format using thetax_query operator
field.To map that to a pretty permalink, you could probably add an operator parameter to the URL, so something like:
Note that sneaky little
or
in the middle there.Thanks. I was hoping to find something already baked into existing URL parameters, so I didn’t have to spend the time learning enough about php and WordPress to do something like this, but I guess I’ll bite the bullet and expend the requisite brainpower. This, at least, provides a basic idea, which simplifies my task a great deal.
I have a long time problem and look forward for your help
I have a custom taxonomy of “town” (it is no hierarchical like tags)
My settings in Permalink admin page:
/%town%/%category%/%postname%/
So this is what I like to accomplish.
mysite.com/town/moscow/category/shopping/ Display all posts in this category in Moscow.
mysite.com/town/moscow/category/shopping/supermarket/ – Display all posts in this -child-category in Moscow.
mysite.com/town/moscow/category/shopping/supermarket/ postname- Display the post.
I’ve added the code in function.php
The result
mysite.com /town/moscow/category/shopping/ It doesn’t!!!!! 400 error
mysite.com/town/moscow/category/shopping/supermarket/ – It doesn’t!!!!!400 error
mysite.com/town/ moscow/category/shopping/supermarket/ postname– It doesn’t!!!!!400error
It seems the sustem doesn’t understand %town%
What is wrong?
Paste the code used to register your custom taxonomy.
This is my code:
It looks like you’ve got the right
query_var
, and the only thing I noticed is that you’re adding'category'
to the query vars when really, you don’t need to. You can changeeg_add_query_vars()
to this:Not sure that will fix the issue. Your best bet is to install Monkeyman’s Rewrite Analyzer and figure out what is and is not matching against your new rules.
I took away the “category” from the code but wordpress doesn’t understand %town% and writes me a mistake: Bad request!Error 400.
May be i’m wrong with the definition of my settings in Permalink admin page:
/%town%/%category%/%postname%/?
Notice I don’t mention Permalink settings anywhere in this article?
You don’t actually need to set
/%town%/%category%/%postname%/
for this to work, and to give you one less thing to test/worry about, I would set it back to a default like/%postname%/
.After the all work has done the links are working that way:
mysite.com/town/moscow/category/shopping/ -works!
mysite.com/town/moscow/category/shopping/supermarket/ – works!
mysite.com/town/moscow/category/shopping/supermarket/ postname- does not work
I’ve tried to solve the task with another option:
In Permalink settings:
town/%town%/category/%category%/%postname%/
But it does not work.
But how can i get the link of the following way
mysite.com/town/moscow/category/shopping/supermarket/ postname
for my posting?
I’m sorry to bother you again!
You’ll need to add a new rule. Something like (not sure if the regex is right):
But that will only work for
example.com/town/moscow/category/shopping/supermarket/
. Ideally, the regex should matche any level of child categories.Thanks for your help, but it doesn’t work.
I have installed Monkeyman’s Rewrite Analizer, and test my link:
example.com/town/moscow/category/shopping/supermarket/postname
On pattern: town/(.+?)/category/(.+?)/?$
Analyzer show:
Town:Moscow
Category-name: /shopping/supermarket/postname
On pattern:
town/(.+?)/category/(.+?)/(.+?)/?$
Analizer show:
Town:Moscow
Category-name: shopping supermarket/postname
I tried to add another rule,
But it doesn’t work.
I can’t spend the time figuring out the correct regex, but just take a look at other rules (as it looks like you’ve done), read up a little on regular expressions and you’ll eventually get it sorted.
As a hint, the regex I gave as an example is too greedy, it captures the forward slash. The regex you provided does include a workaround for that, but also tries to capture a number rather than just a category name. Good luck!
Thank you very much
I’ll do it.
Hi, is it possible to make rewrite rules as: site.com/listing/apartments/us/newyork instead of site.com/listing/propertytype/apartments/country/us/state/newyork?
Hi, I am still find the way to config url as site.com/listing/apartments/us/newyork instead of site.com/listing/propertytype/apartments/country/us/state/newyork. Please help me if it is possible.
Thanks
Hi Hothan, have a read through all the comments as there is information about how this can be done (and why it is very difficult) amidst them.
Hi, thanks for your reply.
The solution provided is quite limited. I have tried another solution, to explode all “/” things and check their’s taxonomy names. It is alright at this time, but it seem not based on wordpress rewrite guide, and might results high volume of mysql query when my database become large.
Hi Brent, thank you so much for this, was enourmously helpful for me!
I’m trying to generate a filter in the sidebar like you have on http://eex.gov.au/events/
The trouble is, that get_terms() outputs all taxonomy terms and not just the ones associated with at least one post of the current query.
Would you mind revealing how you have managed that when I go to e.g. http://eex.gov.au/events/industry/clean-energy-and-renewables/ only those terms show up in the sidebar which are associated with at least one of the queried events? Even with the count behind each term?
This is my current code:
Thank you so much again!
Hi Manuel, thats a post unto itself! If you contact me privately using the contact form I can share the actual code I used, but I don’t want to publish it here without changing it to be more generic.
I’m really interested on this script. Could you share it, please?
Another question… Google webmasters panel shows me a lot of indexing errors related to multiple taxonomies. It’s trying to indexing bad taxonomies term (which don’t exist and are not linked on taxonomies menu) or empty taxonomies url’s (f.e. in /brands/name_of_brand/products/name_of_product/ Google bot is indexing /brands/product/name_of_product).
Is there any way to redirect users when taxonomy doesn’t exist or is empty?
Thanks for your amazing snippets and do our life easier.
Hi jwaisres,
The code from the sidebar can be found in this gist and the relevant functions called by the sidebar are in this gist.
Neither are “plugin-and-play”, meaning, you’ll have to know what you’re doing in PHP to get it to work for your site. 🙂
Regarding Google webmasters, find out where the links to those pages are coming from (from memory Google webmasters will give you that info if you drill down) then remove them.
Thanks a million! I have implemented your eg_update_term_counts function (combined with eg_get_filter_permalink) to do exactly what I needed.
I did this function to ignore pagination when showing taxonomies. It’s not perfect but It works and could be useful: http://pastebin.com/BiXWTAUi
I can’t thank you enough for your help, Brent.
Cool, glad it all helped. Thanks for sharing your function to ignore pagination. 🙂
1 week trying to do this one with multiple dropdowns to filter custom post type by 3 taxonomies but no luck.
Did anyone try it?
Thanks.
Pingback: Advanced taxonomy queries with pretty urls - Jesterland
Hi Brett, I’m trying to wrap my mind around what you’ve written here, it seems like this is exactly what I need to understand to make the site I’m currently working on.. complete. The missing piece of the puzzle, so to speak.
I don’t really want anything coded for me, because I do want to learn something, but would you be able to tell me if this will be able to achieve the results I’ve outlined here:
http://wordpress.stackexchange.com/questions/102585/taxonomy-drill-down-plugin-help-hierarchical-queries-within-plugin
It looks like you’ve done something similar with it, I’m having some trouble getting it to work properly, though.. granted I haven’t really studied it too closely. Some examples of using it in a loop would be immensely helpful, but you’ve really done a lot just by posting this article, so thanks! 🙂
(my in-progress site with stupid complex drill-down: tunagaming.dyndns.org)
… I know your name is Brent. Sorry, long day…
Hi Sallana, I just had a look at your site but can’t see any drill down. Did you remove it?
Regardless, this answer is a pretty good start.
What you really want to do is customise the
WP_Query
depending on the page number being displayed (when the query is for the genre taxonomy). The taxonomy template is probably the easiest way to do that, and as Ravs suggested in his answer. Good luck!I did try his advice… I got stuck on a couple things…
It doesn’t carry over the selected taxonomy term… and when you query multiple taxonomies (ie: yourdomain.com/?taxonomy=term&taxonomy=term) it defaults to archive.php rather than pulling the template for the last term queried.
Maybe I’m just stupid. =/
The drilldown is on the site now, under games. It looks weird ’cause I’m trying to implement pretty permalinks with the code that I’ve used so it’s appending ugly stuff to the end of the pretty URL. heh.
That I can fix. The way that I ended up doing it, is I wrote a PHP function to explode the URL into an array and I pick out terms from the URL and use those in my archive.php code… which is on that answer page now.
I dunno if that’s a clean or good way to do it, I’d love to figure out the proper way but it’s a little aggravating.
Thanks for your reply and help.
Hi – I am so glad I discovered this post, it has solved a big problem i.e. how to filter my WP content by multiple taxonomies at the same time, and how to update links to reflect this.
One question – the taxonomy filters apply to the main loop, but In addition to the main loop, I also have a custom query pulling out a post with a custom field of ‘featured’ (this post is then not duplicated in the main loop). This post is ignored by the filtering method above and so appears all the time regardless of the current filter – is there any way I can make WP ‘see’ it and treat it in the same way as the main loop?
Thanks. 🙂
Hi John, if you want it to be part of the main loop, try using
'pre_get_posts'
filter instead of thequery_posts()
function. There are a few great answers on StackExchange, like this one, which should help.Hi Brent – thanks for getting back to me – I’m not sure how to use pre_get_posts() correctly – can’t quite get my head around where/how to do things with it.
At the moment I have a WP_Query at the top of the page pulling out the featured post, then the normal main query loop following on afterwards (watching not to duplicate the post in the WP_Query.
The problem is that if I try to filter by taxonomies, it filters stuff in the main loop fine, but completely ignores the one at the top.
I’ve tried to find examples of how to run the main loop twice, and example two here: http://wp.smashingmagazine.com/2009/06/10/10-useful-wordpress-loop-hacks/ seemed to be exactly what I wanted, but it didn’t alter the behaviour of my page at all. 😦
If you want to use a custom query, you would need to add the taxonomy query to your
WP_Query
. Because you own/control that query, WordPress doesn’t add in the taxonomy filters for you.Aha, I get it. So I would need to somehow grab the current post filters (if any) and get them into my custom query?
Yep exactly. 🙂
Excellent tutorial!
In my case I’m not looking for this:
“sitename/post-type/taxonomy/term/post-name”
I only want the terms in url like this:
“sitename/post-type/term/post-name”
Is this posible with the code that you post? I’m trying to make it work but the post give me 404 errors…
Excellent article, all work on WordPress 3.6.
if post type ‘post’
'/'
<- dont need, else create wrong rulesThanks for sharing Mihail.
Hi nice post, I have a question thought..
What if you have this kind of url
localhost/this/is/a/very/long/url.php
How can you rewrite this to
localhost/short/url.php
Thanks 🙂
Hi NoobMe, it really depends on the specifics of the content o the long URL (is it to a post? For a category? etc.)
Hi,
I don’t know how to use the Rewrite Rule Generator.
Please, can you explain it to me?
Hello,
Works well for me, thanks for the tutorial, here is my code:
But one question :
I put pagination inside my
archive-produits.php
, it works well too when i navigate to :http://www.mywebsite.com/produits/destinations/new-york
but when i add the second taxonomy and term :
http://www.mywebsite.com/produits/destinations/new-york/types/hotels/page/2
it is broken and display nothing.
Any ideas to put pagination with this.?
You may need to add pagination to the rules. Not exactly sure what that will look like, but this and this may help. As always, Google is your friend. Good luck! 🙂
Yeah, thanks for reply
i did this with no success
‘produits/(destinations|types)/(.+?)/(destinations|types)/(.+?)/page/?([0-9]{1,})/?$’ => ‘index.php?post_type=produits&’ . $wp_rewrite->preg_index(1) . ‘=’ . $wp_rewrite->preg_index(2) . ‘&’ . $wp_rewrite->preg_index(3) . ‘=’ . $wp_rewrite->preg_index(4).’&paged=’.$matches[5],
how do you think?
Not sure, possibly the regex? Try Monkeyman’s rewrite analyzer to see which rules are being matched when you enter in a URL you’re trying to match.
I used your permalink generator, it works like a charm, big up for u man.
I have last question may be stupid?
how to get url like :
ww.webiste.com/post_type/taxonomy-term/taxonomy2-term2
i mean change the “/” by “-”
Thanks
An easy question with no easy answer. 🙂
You’d need to adjust the regex to match the dash-separated URL structure instead of matching the forward-slash separated URL structure. You can then pass the matched term values into the rewrite rules.
Not an easy task!
If you have done your preliminary work, the dog should go
down for you. Go ahead. Dog eaar care is imperative to making sure your dog does not suffer needlessly.
Hi, how would I be able to add the query variable year to the url as well? So I could filter &year=anything and get http://www.example.com/events/location/nsw/industry/web/year/anything
It’s too complicated to do for you George, you’ll need to figure it out. Be sure to share those details in a blog post once you have to help others!
Hello Brent.
Thank you for this excellent tutorial/guide.
Is there a way to make it support rss feed?
Thank you in advance.
Spyros
PS.
I added this but I only get feed for a single custom post type, not for a combination:
AFTER
The code I wrote seems to produce problems.
Taxonomies combinations do not longer function.
If no rewrite rule is set for feed, by just adding /feed/rss I believe wordpress is searching for taxonomy feed and term slug rss.
I can not think of a solution atm.
Pingback: Why won't this rewrite rule work? - HelpDesk