WordPress Custom Query String

Update 02/03/2011:
Got a helpful message from JR about custom taxonomies:
I was searching for a solution for a couple days and finally found a way to go to a custom taxonomy page and further refine the results by post_type. This is critical when you have a situation with one taxonomy servicing multiple post-types. I am hoping to save others the aggravation and apparently the comments are closed on this post.

Thanks for the update JR!

It may not be immediately clear to some where a technique like this might come in handy. So I'm using a recent project as an example. This particular project was for a subsidiary of Milacron, one of Cincinnati's most well-known manufacturing companies. Cimcool develops industrial fluids: additives, rust inhibitors and metalworking fluids are some of their major product lines and those product pages are what we'll use for this tutorial.

Requirements

They already had a website but hired us to handle the SEO. Part of that process involved cleaning up the URL structure of the site by removing query strings from the URLs on product pages. The particular page I'm going to cover in this tutorial is this one. If you visit that link and click any of the categories listed on the left-hand side of the page you'll see that you're sent to a page that looks identical but has the category name in the URL. In fact, all of those pages are the same "MSDS-PIF" page served by WordPress, while the content shown on each of those pages is determined by a query string that is parsed from the last portion of the URL. This allows us to have keyword-rich URLs for the products categories without having to create separate WordPress pages for each category.

Problem

The biggest problem with getting this done in WordPress, as I alluded to in the last paragraph, is that even with pretty permalinks enabled, we would have had to create a new WordPress page with a unique template for each of those product categories. There were only 15 product categories at the time, so that wouldn't be such a tedious task, until you consider the fact that it makes adding categories a bit of a hassle, in that we'd have to create a new WordPress page and page template anytime the company would need to add categories. While that would mean more billable hours for us, it wouldn't be in the client's best interest if it could be avoided.

The Solution

Thankfully, it could be avoided. Instead of creating a bunch of pages and their respective page templates every time new categories are added, we can use WordPress' ability to specify custom rewrite rules to pass the query string as part of the URL and use a single page and page template to display all product categories.

The Process

The first step was to create a function to add variables to WordPress' query string and then hook that function into the query_vars hook.

Next, we created a function to add rules to WordPress' existing array of rewrite rules and hook that function into the rewrite_rules_array hook.

Finally, in the MSDS-PIF page template, we used the $wp_query object's query_vars property to get the query string from the URL.

The Code

So let's get down to the code. First we need to create a function to tell WordPress to keep track of our new query variables. This function goes in your theme's functions.php file.

function add_query_vars($aVars) {
$aVars[] = "msds_pif_cat"; // represents the name of the product category as shown in the URL
return $aVars;
}

// hook add_query_vars function into query_vars
add_filter('query_vars', 'add_query_vars');

The above process is fairly simple. When the function is hooked into query_vars, WordPress passes the existing array of query variables to the function. We simply add "msds_pif_cat" as another query variable and send the array back to WordPress.

Now WordPress knows, in addition to it's existing query variables, it should add one called "msds_pif_cat," which we'll use to store the product category name as specified in the URL.

However, this alone won't do the trick. We also need to tell WordPress how to populate that query variable. In Cimcool's case, we want the product category pages' URLs to look like this: http://www.cimcool.com/msds-pif/category-name/. So, we need to tell WordPress that anytime it finds a URL matching that structure, to use the last portion, the category-name, to populate our new "msds_pif_cat" query variable. To do that we need to add a custom rewrite rule.

We accomplish this with another function in our theme's functions.php file:

function add_rewrite_rules($aRules) {
$aNewRules = array('msds-pif/([^/]+)/?$' => 'index.php?pagename=msds-pif&msds_pif_cat=$matches[1]');
$aRules = $aNewRules + $aRules;
return $aRules;
}

// hook add_rewrite_rules function into rewrite_rules_array
add_filter('rewrite_rules_array', 'add_rewrite_rules');

The function is hooked into rewrite_rules_array, so WordPress passes the existing rewrite rules as an array to the function, which we then retrieve as $aRules.

We then add a new rule in the form of an array: array('msds-pif/([^/]+)/?$' => 'index.php?pagename=msds-pif&msds_pif_cat=$matches[1]')

This rule tells WordPress that anytime it finds a URL that includes "msds-pif/", followed by anything other than a forward slash, followed by another (optional) forward slash, to capture the last portion of that URL (the part within the parenthesis), and serve the page found at "index.php?pagename=msds-pif&msds_pif_cat=$matches[1]".

By default, WordPress will store the captured portion of the URL in an array called $matches. And so if we have a URL request like "http://www.cimcool.com/msds-pif/industrial-cleaners/", WordPress will serve the page at "index.php?pagename=msds-pif&msds_pif_cat=industrial-cleaners".

This allows us to capture that msds_pif_cat variable on the MSDS-PIF page template and vary the content depending upon the contents of that variable.

Getting the Query Variable

We can't just use the $_GET superglobal to grab our msds_pif_cat query variable. Instead, we have to access the query_vars property of the $wp_query object.

So, on our MSDS-PIF page template, we'll access that variable using the following code:

if(isset($wp_query->query_vars['msds_pif_cat'])) {
$sMsdsCat = urldecode($wp_query->query_vars['msds_pif_cat']);
}

Next steps

In our case, we then pull all products matching the category name as stored in the $sMsdsCat variable from the database and display them on the page. Obviously what you do with that variable is up to you and, from this point on, is no different than if you had used PHP's $_GET superglobal to get a query variable on a non-WordPress page.

One thing I should mention here is that the rewrite_rules_array hook is called when you update or save your permalink structure, so after you make these changes, you'll have to re-save permalinks to see the changes take effect.

More examples

Take a look at the Cimcool link above to see all this in action. For an example of using multiple custom query variables, have a look at Screen Innovations' dealer locator, where we used a similar process to display dealers on the page for a given country, state and city using SEO-friendly URLs and without the need to create a hundred or so page templates.

Finally, check out the following resources for more information on URL rewriting in WordPress:
http://codex.wordpress.org/Function_Reference/WP_Query
http://www.prodeveloper.org/create-your-own-rewrite-rules-in-wordpress.html
http://www.seodenver.com/custom-rss-feed-in-wordpress/