Dirty hack: force primary category for wordpress post permalink

The permalink from Wordpress CODEX

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!

2 Responses to “Dirty hack: force primary category for wordpress post permalink”

Leave a Reply