Dirty hack: force primary category for wordpress post permalink
I’m not especially proud of this hack. However, I was getting crazy by how the category used for a post permalink is choosen. And I could not figure out a clean way to control which is the primary permalink (I hope it’s not part of Wordpress 3.0…).
You may need to set the primary category for permalink in two situations:
- First, you want some consistency among your posts, having them in the same primary category.
- Second, it’s never fun when, by adding an additional category to an existing post, it changes the post permalink!
If you wonder how the “primary” category for a permalink is defined by wordpress, the answer is quite simple: they are ordered by ascending ID. So, if you add an old category to an existing post, your existing post will have a new permalink. And if, like me, you’re using wordpressMu, you’ll discover that a newly added category in a blog may have a very low ID because this category (or tag…) has already been used by any of the other blogs.
The dirty hack
It’s really not a code-hack I’m proud of. However, as I needed something, I believe some others may need it too, that’s why I’m sharing it.
To be on the safe side, I recommend to leave this hack in a standalone file (with my active theme files, in my case), and include it in the functions.php file.
The idea is to alter the “the_permalink” filter. So, let’s add the filter:
add_filter('the_permalink', 'custom_cat_order');
Then, the custom filter function:
// Order by cat_order, otherwise by name, assuming it's a post permalink (dirty!) function custom_cat_order( $content ) { global $post; // in case no post data found, do nothing if ( @empty($post->ID) ) return $content; // otherwise, assuming current post is the right post (dirty!) // custom/primary slug cat order defined here (dirty!) $cat_order = array("directory", "bonus", "blog"); // add all remaining categories (with duplicates, dirty!) $all_cats = get_categories(); // Build an array with categories slug foreach( $all_cats as $cat) $cat_order[] = $cat->slug; $cats = get_the_category($post->ID); if ( $cats ) { // Build an array with Post categories slug foreach( $cats as $cat) $post_cats[] = $cat->slug; $the_cats = array_intersect($cat_order, $post_cats); usort($cats, '_usort_terms_by_ID'); // order by ID $category0 = $cats[0]->slug; $the_cat = array_shift($the_cats); // Assuming category is in-between slashes (dirty!) $content = str_replace("/".$category0."/", "/".$the_cat."/", $content); } return $content; }
So, basically, I’m defining the categories with higher priority in the $cat_order
array, and at the end search/replace the old category slug by the one with higher priority.
As you can see, it’s quite dirty… So if you figure out a cleanest way to do it, please let me know!
Just thought you should know, there’s now a plugin that seems to do this quite well: http://wordpress.org/extend/plugins/hikari-category-permalink
Thanks Ash, sounds very good !