Define permalinks for custom post type by taxonomy in WP 3.4

Solution

I have figured it out with the help of these posts by Jan Fabry ->

https://wordpress.stackexchange.com/a/5478/10350
https://wordpress.stackexchange.com/a/22490/10350

I have set up the custom post type as follows ->

  • Custom post type (register_post_type) -> “product”
  • Taxonomy (register_taxonomy) -> “product-type”

Back-end function

I rewrite the permalink structure that is saved in the back-end, so that the permalink is saved to include the custom taxonomy type – “product-type”

add_filter('post_type_link', 'product_type_permalink', 10, 4);
function product_type_permalink($post_link, $post, $leavename, $sample) {
    //If is custom post type "product"
    if ($post->post_type == 'product') {
        // Get current post object
        global $post;
        // Get current value from custom taxonomy "product-type"
        $terms = get_the_terms($post->id, 'product-type');
        // Define category from "slug" of taxonomy object
        $term = $terms[0]->slug;
        // Re-structure permalink with string replace to include taxonomy value and post name
        $permalink = str_replace('product/', 'product/' . $term . "https://wordpress.stackexchange.com/", $post_link);
    }
    return $permalink;
}

Set the permalink setting to ‘Post name’ and save. If you add a product to a category and save, it should re-write the permalink to include the customy taxonomy definition, in this case – “product-type”. So if you add “Red Chair” to the “Chairs” category, the URL will be formatted as follows ->

http://website.com/product/chairs/red-chair/

But if you try to go to this page, you will get a 404 error. That is because wordpress doesn’t yet know how to query the database with this URL, so you have to write it.

Front-end function

We need to add rewrite rules so that wordpress can take our url and query the database. We use the wordpress function add_rewrite_rule to translate the permalink given into a query string.

add_rewrite_rule(
    // The regex to match the incoming URL
    'product/([^/]+)/([^/]+)/?',
    // The resulting internal URL: `index.php` because we still use WordPress
    // `pagename` because we use this WordPress page
    // `designer_slug` because we assign the first captured regex part to this variable
    'index.php?product-type=$matches[1]&product=$matches[2]',
    // This is a rather specific URL, so we add it to the top of the list
    // Otherwise, the "catch-all" rules at the bottom (for pages and attachments) will "win"
    'top'
);

In this function, the matches array is defined by WordPress exploding the given string at each slash. So in this example, the regular expression ([^/]+) is used to match anything in between each slash. In this example there are 2 levels, so it matches product type, then the product, and adds them to the matches array, where product-type = $matches1, and product = $matches2.

This rewrite rule translates this ->

product/chairs/red-chair/

Into this ->

index.php?product-type=chair&product=red-chair

Which our database can use to query the database and return the correct product page with the formatted permalink.

This throws off product-type pages as there will only be one level in the url, product-type. This means that the rewrite rule will currently always try and identify the product name as defined in the query string. So for this we also write a single level rewrite rule:

add_rewrite_rule('^product/([^/]*)?$','index.php?product-type=$matches[1]','top');

This will now also query for product type pages so we can just loop through the taxonomy as normal if we want to display the various product types, without throwing 404 errors when we try and link to them.

Downside

This will currently only take a single level of taxonomy, so the custom taxonomy structure can not be hierarchical. If you specify more than one taxonomy, it will use the most one with the first ID to define the permalink. A potential workaround for this is to hide the custom taxonomy menu that appears in the side bar of custom post types, and add a meta box for taxonomy where only a select box can be used. I use the Meta Box plugin for this. (nb, this plugin doesn’t have any admin menus, it allows you to write meta boxes for custom post types in your functions.php just by creating arrays – highly recommended!)

Leave a Comment