The WordPress custom taxonomy system allows you to create your own grouping of WordPress objects (e.g. posts, pages, and custom post types). Tags and categories, for example, are taxonomy objects that are native to the WordPress system. With the custom taxonomy framework, you can create your own tags and categories.
In the article Custom Post Type Permalinks (Part 2), Lane and Andrew asked the very good question of how we can add custom taxonomy tags to WordPress permalinks.
For example, if we have a custom taxonomy called rating we might want to set our WordPress permalink structure to –
/%rating%/%postname%
Here we consider how to insert custom taxonomy tags into the WordPress permalink structure.
1. Create a Custom Taxonomy
First, we create a custom taxonomy object called rating with the register_taxonomy WordPress function.
add_action('init', 'my_rating_init'); function my_rating_init() { if (!is_taxonomy('rating')) { register_taxonomy( 'rating', 'post', array( 'hierarchical' => FALSE, 'label' => __('Rating'), 'public' => TRUE, 'show_ui' => TRUE, 'query_var' => 'rating', 'rewrite' => true ) ); } }
Setting the rewrite argument to true (Line 10) will automatically add the tag %rating% to our WordPress system.
However, if we try to set our blog permalink structure to –
/%rating%/%postname%
Our new post permalinks will look like this
http://shibashake.com/wordpress-theme/%rating%/test-1
As a result we will get a 404 file not found error. This is because our %rating% tag was not properly translated.
2. Translate Our Custom Taxonomy Tag
To translate our new %rating% tag, we must hook into the permalink generation function – get_permalink.
The post_link hook allows us to translate tags for regular post objects and the post_type_link hook allows us to translate tags for custom post type objects.
Note that both filter hooks accept exactly the same arguments, so we can tie both hooks to the same function.
add_filter('post_link', 'rating_permalink', 10, 3); add_filter('post_type_link', 'rating_permalink', 10, 3); function rating_permalink($permalink, $post_id, $leavename) { if (strpos($permalink, '%rating%') === FALSE) return $permalink; // Get post $post = get_post($post_id); if (!$post) return $permalink; // Get taxonomy terms $terms = wp_get_object_terms($post->ID, 'rating'); if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug; else $taxonomy_slug = 'not-rated'; return str_replace('%rating%', $taxonomy_slug, $permalink); }
Line 5 – If the permalink does not contain the %rating% tag, then we don’t need to translate anything.
Line 12 – Get the rating terms related to the current post object.
Line 13 – Retrieve the slug value of the first rating custom taxonomy object linked to the current post.
Line 14 – If no rating terms are retrieved, then replace our rating tag with the value not-rated.
Line 16 – Replace the %rating% tag with our custom taxonomy slug.
Our new post permalinks will look like this –
http://shibashake.com/wordpress-theme/rated-a/test-1
Kevin says
First off I want to thank you for writing this post. I’ve been researching this issue for days. It’ took my a long time to find this but so happy I finally did. Below I took your awesome function and made it fully dynamic. It should search through and replace as many un-translated customer slugs. I hope this helps everyone. Let me know what you think.
add_filter('post_link', 'rating_permalink', 10, 3);
add_filter('post_type_link', 'rating_permalink', 10, 3);
function rating_permalink($permalink, $post_id, $leavename) {
$matches = array();
$t = preg_match_all('/\%(.*?)\%/', $permalink, $matches, PREG_SET_ORDER);
// If no matches were found simply return the original permalink
if (empty($matches)) return $permalink;
// If matches were found translate them
// Get post
$post = get_post($post_id);
if (!$post) return $permalink;
foreach ($matches as $match) {
// Get taxonomy terms
$terms = wp_get_object_terms($post->ID, $match[1]);
if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0]))
{
$taxonomy_slug = $terms[0]->slug;
}
else
{
$taxonomy_slug = $match[1];
}
$permalink = str_replace($match[0], $taxonomy_slug, $permalink);
}
return $permalink;
}
Rajan says
hi admin,
acc. to your tutorial i added a unique tag into custom permalink structure. for example i added :- /%categoryProperty%/%postname%/.
but the permalink is still having post name only. /%categoryProperty%/ is not showing in permalink. can you suggest me what the wrong is happened?
ShibaShake says
I would look at the post_link and post_type_link function to see what is being passed in and what is being passed out.
Diana says
Thanks a lot! I was afraid that this could ever be done. I built different permalinks for different depth:
hotels/europe/france/paris/a-post-about-a-hotel
and shallow itens such:
places/europe/france/10-parks-you-should-visit
places/europe/france/top-cheap-stores
I just needed to build the link:
$taxonomy_slug = $terms[0]->slug.'/'.$terms[1]->slug
Thanks a lot!
lance says
any idea how to add a username in a link?
for exampe. site url: http://mysite.com/
now adding a username makes it http://mysite.com/username
note. the username can be change e.g john, james or jones.
now for example we have a username john -> our url now is http://mysite.com/john
and if we visit other page like contact, the username should be ther on a link e.g http://mysite.com/john/contact.html
but if theres no username on the link. e.g http://mysite.com/
the contact page should only be http://mysite.com/contact.html
hopefully you could help me. cheers!
ShibaShake says
Probably can just use the %author% tag and then customize it as necessary.
Lehooo says
Thanks, this was very helpful! Works like a charm!
Ryan says
I don’t follow this, I can’t tell what I’m suppose to leave the same and what I am suppose to replace with my custom post type name and taxonomy name.
Stefan Vorkoetter says
I’ve tried this technique to help me migrate an old static-HTML web site to WordPress, without having to change any of the old page URLs. I created a custom taxonomy that corresponded to the directory structure of the old site. It works great, allowing me to give all my _posts_ the same URL that my static web pages used to have, except for one serious problem:
All of my _page_ permalinks no longer work.
Although this should not have affected page permalinks at all, it seems to have broken them. WordPress still thinks that they have not changed, but trying to go to them takes me to my 404 page instead. Does anyone have any ideas what might be wrong?
ShibaShake says
That is likely due to permalink conflicts.
http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2#conflict
Stefan Vorkoetter says
Okay, thanks, that makes sense. It’s not clear to me how to resolve this though, since I can’t just insert a unique slug into the structure (since my goal is to make the page URLs exactly the same as they were on the static site, which did not contain that unique slug).
ShibaShake says
Another solution is to just add a unique character instead of an entire slug, for example –
Note the underscore added to the beginning of the regex structure. I haven’t looked into this in great detail but playing around with the regex associated with each of the terms may bring some interesting solutions. I have tried the underscore solution on taxonomies, and it does work.
One can probably also change the regex structure for a ‘page’, but that is not something I have tried, so I don’t know how easy/difficult it is.
Stefan Vorkoetter says
One (less than ideal) solution I just found is to make _all_ of my pages children of some (never displayed) parent page. This effectively adds a slug to the beginning of each parent page permaling, thus making it no longer regex-match my custom post permalink structure. Suggestions for a better idea are still appreciated.
Stefan Vorkoetter says
Just found a fix that works!
This removes the rewrite rule that causes a bare page reference to be interpreted as a folder reference.
Saulo Padilha says
Stefan, you just saved my day. I was looking for that fix for more than 6 hours!
thanks for share!
Alex Nowak says
Hi Stefan!
The one you posted works perfect but do you have a clue what would be the right rule for child pages?
Marius says
I’ve tried it, it works perfectly, except for one thing: if the taxonomy is hierarchical, it only puts the first level in the URL. Can you tell me how to change the PHP so that it works on hierarchical taxonomies by having the path /parent/child/child/postname?
Thx!
ShibaShake says
You may need to traverse the parent relationship manually by using a loop.
Steffen says
First… thanks for your informative article.
I tried your suggestion but i get always an 404.
http://example.com/my-custom-post-type/taxo/child-of-taxo/post-title -> 404
If i delete the slug ‘child-of-taxo’ manually from the adressbar everything works fine.
http://example.com/my-custom-post-type/taxo/post-title -> 404
This the code i’m using:
add_filter('post_link', 'taxo_permalink', 10, 3);
add_filter('post_type_link', 'taxo_permalink', 10, 3);
function taxo_permalink($permalink, $post_id, $leavename) {
if (strpos($permalink, '%taxo%') === FALSE) return $permalink;
// Get post
$post = get_post($post_id);
if (!$post) return $permalink;
// Get taxonomy terms
$terms = wp_get_object_terms($post->ID, 'taxonomie');
if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) {
$slug1 = $terms[0]->slug;
$slug2 = $terms[1]->slug;
$taxonomy_slug = $slug1 . '/' . $slug2;
} else {
$taxonomy_slug = 'other';
}
return str_replace('%taxo%', $taxonomy_slug, $permalink);
}
I’m sure you are a busy man but i would appreciate a suggestion how to fix this problem. Do you have an idea?
ShibaShake says
I would try doing it with two separate tags – %taxo% and %childtaxo%
Rob says
As before, Shibashake, I very much appreciate all your help and patience!
1) While that didn’t result in an error, the links did not function properly. Posts within a Taxonomy, and their URLs, worked perfectly. Posts within a Category worked perfectly, but their URLs removed the category entirely, leaving two slashes in the title (e.g.; site.com//postname/
2) I’ll have to dig further into this fix — or hire someone smarter than myself to help š [I feel bad for hijacking your thread with my many comments, but if interested / available / affordable, I’d be honored if you took a stab! I sent you an email through your site with the same]
ShibaShake says
Hello Rob,
Sorry for the late reply. Have been busy with dog matters lately.
I am currently not working on external projects, but will be happy to answer whatever questions that I can. Good luck with your site.
Rob says
no worries, shibashake – hope the dogs are well!! regarding my permalinks, i can’t thank you enough for getting me this far!! unfortunately, heading through the next steps is just a bit (a lot!) beyond my brain. i can’t quite get the “no taxonomy, then category” part working, and hooking into the request filter is an entirely foreign concept to me. if you had anything easy to further suggest, i’m happy to give it a try! but i don’t want to completely hijack your post with my dumbness š
ShibaShake says
Hi Rob,
Your site looks very awesome!
If you send me the file you use to modify your permalinks, I can probably play around with it some to get the category thing working. I will send you an e-mail.
Rob says
i recently created a “cartoons” taxonomy, with all different cartoon names as part of the group: pokemon, smurfs, thundercats, etc. most of my posts have a “cartoon” that they are associated with. however, there are some posts that are not part of a cartoon taxonomy.
without knowing the proper nerd speak (i’m only an unproper nerd), i’d love the permalink logic to act something like this:
if this post has a taxonmy, make the url:
/%cartoons%/%postname%/
else this post does NOT have a taxonomy, make the url:
/%category%/%postname%/
is that feasible using your code, above…? and if so, what would i enter in SETTINGS > PERMALINK ?
ShibaShake says
Hmmm, you could try using /%cartoons%/%postname%/ as your main permalink and then in the rating_permalink/cartooon_permalink function, insert in the post category instead of ‘not-rated’.
Rob says
hi shibashake ā first off, youāre the best š
with that out of the way, i implemented as you detailed above, and it worked VERY well, but not perfectly. all of the posts within a cartoon taxonomy moved to their new URL without issue, which was awesome.
however, i hit two issues with the rest:
1) how do i change āelse $taxonomy_slug = āno-ratingā;ā to force it to pull up the respective category slug? (as a quick fix, iāve changed it to just the general term ācartoonā, which isnāt so bad!)
2) iāve run into the 404 issue with pages. reading through the comments, i saw that you link people to: http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2#conflict however, im hoping to avoid adding the extra portion of the slug (thatās the whole purpose of this change, for me, anyway). one page i have, for example, is ātoonbarn.com/gamesā which results in the 404. even if i change the slug of that page to ātoonbarn.com/gamessuperfaceā ā which i did! š ā i still get the 404 upon entry. is that still part of the same issueā¦?
Rob says
[also, thanks for making me a hot girl! š ]
ShibaShake says
LOL! You are welcome. š
ShibaShake says
Hello Rob –
1. Try using get_the_category
http://codex.wordpress.org/Function_Reference/get_the_category
2. The 404 error occurs because 2 different objects share the same regular expression structure. Changing the actual name of the page or even changing the permalink tag names %my_taxonomy% will not solve the issue because the regular expression structure is still similar.
You can quickly identify if it is a regular expression structure issue by adding in the slug and seeing if that solves the problem. Alternatively, you can hook into the ‘request’ filter and see what arguments are being passed into the query. There is an example of the request filter here –
http://shibashake.com/wordpress-theme/mastering-the-wordpress-loop
Rob says
thanks again for the help, shibashake
1) unfortunately, i couldn’t get this to work. i’m likely just not smart enough to get the proper php phrasing when replacing the “else $taxonomy_slug” with something like “get_the_category”. The closest I could get was the URL coming up with the term “Array” as that portion of the slug. In the meantime, seems like it’s just easiest for me to keep the “fake taxonomy” of just the simple “cartoon”
2) as you said, I tried adding another term to the front of the permalink structure, and it had everything working again! the pages AND the posts with the new custom URL. Now that it is identified as an expression structure issue… does that mean its possible to bypass? I really only have three or four pages, so I’d love to be able to get to everything without the extra slug piece (though it wouldn’t be the end of the world if it had to be)
ShibaShake says
Hello Rob,
1. Try something like –
instead of else $taxonomy_slug = ‘not-rated’;
2. You could solve the 404 issue by making the regular expression structure unique. For example we could add unique characters into the taxonomy name. Another possibility is to hook into the request filter and resolve it manually there. Both are fairly complex though.