Change URL structure of custom post type and hierarchical custom taxonomy in WordPress
Hi again!
It’s been a few months since I last posted and a lot has changed here at WSI which has meant less time to do these awesome blog posts! But here’s one which should whet your appetite! (Whet is spelled correctly 😉 )
You have just created a super cool and useful Custom Post Type in WordPress and added it to an equally useful Custom Taxonomy which is hierarchical but you find that your URL structure just isn’t how you want it. You want it to look pretty AND be SEO friendly! It’s not immediately obvious how you might achieve this, so lets get stuck in.
First lets set the scene. We are creating a site which displays golf courses by location. A location can be a child of another location. For example you could have Scotland as a location and St Andrews as a location. St Andrews is a location in Scotland so Scotland becomes the parent. We want this relationship to be represented in the URL structure. The URL structure will look something like:
domain.com/golf-courses/scotland/st-andrews/old-course
where scotland and st-andrews are terms in the location taxonomy and old-course is a golf course which is a custom post type
We have a Custom Post Type called ‘Golf Course’. This lets us add new golf courses in the same way that we add posts. This is how you do that, pop this code into your themes functions.php file :
register_post_type('golf-courses',
array(
'label' => __('Golf Courses'),
'description' => 'Used to display golf courses',
'capability_type' => 'post',
'show_ui' => true,
'public' => true,
'publicly_queryable' => true,
'rewrite' => array('slug' => 'golf-courses/%location%', 'with_front' => false),
'taxonomies' => array('location')
)
);
The important options to note are rewrite and taxonomies. Rewrite tells WordPress that we want the URL structure to be golf-courses/%location%, location being a taxonomy that the golf course belongs to. There are more options available to register_post_type so have a play around. This won’t work yet, we still have work to do!
Now let’s create our Custom Taxonomy:
register_taxonomy('location',
array(
0 => 'golf-courses',
),
array(
'hierarchical' => true,
'label' => __('Location'),
'rewrite' => array('hierarchical' => true, 'slug' => 'location'),
)
);
Here the important option is hierarchical which lets a location be the child of another location. I usually like to put these in their own function and hook it as an action to be executed during init.
add_action('init', 'create_hierarchy');
function create_hierarchy(){
register_post_type('golf-courses',
array(
'label' => __('Golf Courses'),
'description' => 'Used to display golf courses',
'capability_type' => 'post',
'rewrite' => array('slug' => 'golf−courses/%location%', 'with_front' => false),
'taxonomies' => array('location')
)
);
register_taxonomy('location',
array(
0 => 'golf−courses',
),
array(
'hierarchical' => true,
'label' => __('Location'),
'rewrite' => array('hierarchical' => true, 'slug' => 'location'),
)
);
}
Now comes the fun bit! Here we will create the function which manipulates our URL (permalink) structure. It looks a bit daunting, but I’ll explain it in the comments.
function create_hierarchy_permalink( $post_link, $id = 0, $leavename = false ) {
// if we discover the string %location% in our url then continue
if ( strpos($post_link, '%location%') === false ) {
return $post_link;
}
// if this link is also a golf course continue
if ( strpos($post_link, 'golf-courses')){
$post = get_post($id);
// make sure that the current post is actually a golf course
if ( !is_object($post) || $post->post_type != 'golf-courses' ) {
return $post_link;
}
// get all the locations that this golf course belongs to.
// It could belong to Scotland AND St Andrews
$terms = wp_get_object_terms($post->ID, 'location');
if ( !$terms ) {
return str_replace('golf-courses/%location%/', '', $post_link);
}
// loop through each location adding it to the url
$locations = "";
foreach ($terms as $term) {
$locations .= $term->slug . "/";
}
$locations = trim($locations, "/");
// return the original link, but with the string %location% replaced
// with a hierarchical location:
// golf-courses/scotland/st-andrews
return str_replace('%location%', $locations, $post_link);
}
}
add_filter('post_type_link', 'create_hierarchy_permalink', 1, 3);
The final line add_filter means that we call this function whenever WordPress generates a permalink. Our code manipulates that permalink and returns it in the structure that we want. You can do all sorts of crazy things in that function, your imagination is the only limit!
I’d love to hear any feedback or questions.
Richard
Nice one Kirk and thanks, this will come in useful for many projects where SEO friendliness and a super user friendly admin section are priorities.
Ian
Very nice simple, straight forward write up Kirk. A couple of things that might be worth adding:
In register_post_type:
'show_ui' => true
( to display it in the admin of WP )
'public' => true,
( so it’s visible! )
'publicly_queryable' => true,
( and queryable )
These 3 additions will “flesh out” the bare basics of what is required to have a post type and taxonomy visible/usable in the backend and queryable/displayable in the front-end.
Unfortunately though, the hierarchy in the URLs are lost. I created 3 categories:
Brisbane
|– Toowong
|– Birkdale
/location/brisbane/ = accessible, URL good.
/location/birkdale/ = accessible, URL lost hierarchy
/location/toowong/ = same as Birkdale
For the last two, I can *manually* inject /brisbane into the URL and it works, but it doesn’t generate that by default.
Additionally, looking at a post URL:
/golf-courses/toowong/toowong-public/ – same deal. doesn’t generate the hierarchical fragment for the parent tax, but IS accessible via that URL if you inject the parent tax in manually, ie:
/golf-courses/brisbane/toowong/toowong-public/
This is one of the best walkthroughs I found without junking everything up and adding every parameter under the sun, but unfortunately, WP just doesn’t seem to fundamentally get the idea of properly hierarchical URL structures!
My personal total time to run this tutorial, test & feedback: 35 minutes. Great job Kirk!
Komba
Only one thing:
What do I have to do to access domain.com/golf-courses and show all the terms?
Now, I get a 404 error.
Thanks!
Alexandre Pereira
This is by far the most well explained post on the topic I’ve read so for. I tried and it works I have a question though; I will explain my scenario and your help would be really appreciated if you could.
I have the same scheme on a real estate website. I’ve added custom post time called property and inside property I’ve added quite a few taxonomies but the most important 3 are.
– City (property_city)
– County (property_county)
– Condominium (property_contominium)
They are all independent taxonomies, meaning none of them are child of others so what is happening in the permalink is the following:
mywebsite.com.br/city
mywebsite.com.br/county
mywebsite.com.br/condominium
For SEO purposes I would like to make the hyperlink of the custom taxonomy for the county and condominium to be something like this:
mywebsite.com.br/city/county
mywebsite.com.br/city/county/condominium
Is it possible to attain that result with the taxonomies not being child of one another?
When I saw your function I tried to edit it so I could get something like this:
mywebsite.com.br/%city%/county
mywebsite.com.br/%city%/%county%/condominium
With no success.
Am I jumping the gun here or could this be attained?
I would really appreciate if you could help!
anti inflammatory tick repellent for dogs natural
Thanks for finally writing about > Change URL structure of custom post type
andd hierarchical custom taxonomy in WordPress | WSI < Liked it!