By default WordPress will reuse terms with the same name and slug between different taxonomies (at least for now). For instance, if you add the term ‘red’ as a tag, and then add the category ‘red’, the same wp_terms
record will be shared between them. This is also true for any custom taxonomies you define (remember that despite their seeming differences, WordPress tags and categories are both implemented as taxonomies).
Now, in general this is fine; if the name and slug are the same, then just given the fact that all term records are stored in a single table forces them to be shared across different taxonomies. However, what if you don’t care so much about the slug, and want to be able to use the same term name within multiple taxonomies, but don’t want the wp_terms
record shared?
I’ll admit this might not be a typical case, but it came up for me recently when doing some WooCommerce work, specifically because I wanted to be able to attach different term meta to each of the terms with the same name, so they couldn’t be shared between taxonomies. (Side note: WooCommerce allows you to attach meta information to term records, a really cool extension to WordPress with lots of uses, and I plan to cover that implementation in a future article.)
One simple way to stop the records from being shared is to make the admin user define a unique slug for each term that shares a common name with another taxonomical term, ie: ‘Red’, ‘red’ in taxonomy ‘A’ and ‘Red’, ‘red-2’ in taxonomy ‘B’, etc. Obviously the drawback is that you have to remember to do this, have to remember how many different ‘red’ terms there are, all easy stuff to forget until it’s too late.
So, as usual I’ve put together a little modification that you can add to your theme’s functions.php, or to your very own custom site plugin and explained below.
The Code
The implementation is actually fairly simple: we hook a filter to ‘pre_insert_term’ and temporarily rename the term as needed to generate a unique slug and thus a separate term record, then after the term is created we fix the name back to what the user originally specified. This is done only if a term slug is not given; we assume the user had something special in mind if they went through the trouble of providing a slug. And with that, here’s the code:
/** * This filter temporarily renames any terms to force a unique slug when they * would otherwise not be unique. This is to overcome a "feature" with * WordPress, whereby terms are recycled among different taxonomies rather than * being created new with a unique slug. As an example, if the term 'red' * exists in a taxonomy and 'red' is added to another taxonomy, it will be * temporarily named as 'red 2', which forces a unique slug to be created, then * we remove the name counter suffix after the term is created. * * @param string $term the term name * @param string $taxonomy the taxonomy name * @return string the term name */ function skyverge_pre_insert_term( $term, $taxonomy ) { // if no slug was specified if ( empty( $_REQUEST['slug'] ) ) { $slug = sanitize_title( $term ); // check whether a unique slug needs to be generated $new_slug = wp_unique_term_slug( $slug, (object) array( 'parent' => 0, 'taxonomy' => $taxonomy ) ); if ( $new_slug != $slug ) { // append the unique slug counter to the end of the term name, to force uniqueness $term .= ' ' . substr( $new_slug, strrpos( $new_slug, '-' ) + 1 ); // hook so we can fix the term name back to what it should be add_action( 'created_term', 'skyverge_created_term', 10, 3 ); } } return $term; } add_filter( 'pre_insert_term', 'skyverge_pre_insert_term', 10, 2 );
As you can see, after changing the term name if needed, a second action is added to fix the modified term name. The implementation of that function is as follows:
/** * This action removes the temporary counter which is added to terms to force * uniqueness. * * See the commends for the skyverge_pre_insert_term() function * for the full details. * * @see skyverge_pre_insert_term() * * @param int $term_id term identifier * @param int $tt_id term taxonomy identifier * @param string $taxonomy taxonomy name */ function skyverge_created_term( $term_id, $tt_id, $taxonomy ) { $term = get_term( $term_id, $taxonomy ); // get rid of the temporary counter we added to the name $name = substr( $term->name, 0, strrpos( $term->name, ' ' ) ); // update and we're done! wp_update_term( $term_id, $taxonomy, array( 'name' => $name ) ); }
And that’s the whole of it. If there’s anyone else out there who did not want their terms shared between taxonomies, let me know in the comments below, I’d be curious to hear what you’re doing!
Please see this example
Cpt 1: Hotels
Taxonomy: Hotels Destination >> New Delhi / Colombo / Paris / London
Cpt2: Tours
Taxonomy: Tours Destination >> New Delhi / Colombo / Paris / London
WP Page for Destination Info + hotels + tours >> New Delhi / Colombo / Paris / London
now will having a common title for taxonomies and page hurt my seo?
nah, I think you’ll be fine
real thanks!!
just to confirm..this will be fine together?
mywebsite.com/hotels/paris/ [listing only hotels in paris]
mywebsite.com/tours/paris/ [listing only tours in paris]
mywebsite.com/paris/ [all sort of info on paris]
Looks fine to me! It really depends on what makes sense for the Information Architecture (IA) of your site. Is the focus going to be hotels and tours in multiple cities, among them paris? Then the structure you showed would make a lot of sense
Hi Justin and Amit
Justin great blog post, really got me thinking about my structure in navigation closer. Im using the types WP plugin for creating custom taxonomies and custom post types.
Im building a website directory and stuck in the same situation you probably heard many times before. This is my structure.
Town 1- (custom parent taxonomy)
-cuisine-(terms)
-hotels-(terms)
-services-(terms)
Town 2- (custom parent taxonomy)
-cuisine-(terms)
-hotels-(terms)
-services-(terms)
Town 3- (custom parent taxonomy)
-cuisine-(terms)
-hotels-(terms)
-services-(terms)
Now my issues is that as you can see i have the same terms in various taxonomies. However i see that types wordpress plugin allows a custom slug rewrite which will allow me to have a different URL permalink for all the terms. so i could make the permalink for instance town 3 would be.
domain.com/town-3/cuisine-town-3
and for town 2 i would use
domain.com/town-2/cuisine-town-3.
Correct me if im wrong, can we not just link the same term to various taxonomies?
or will i have to go the long route and create separate taxonomies and totally different terms like im suggesting in the example above.
i really dont want to mess with my DB tables. So im hoping that i can just use the same name term with a different url slug for each term cuisine. In the example above i have created the term cuisine 3 times for each taxonomy, the only difference is the slug and ids. I need the name term to remain the same as all names in taxonomies will read cuisine. Please help this would be greatly appreciated.
Hey Wayne, well by default WordPress will share taxonomical terms when possible, which makes sense as a term is just a name and a slug. The slug is what determines whether a term is new or not; the name can be whatever you want. Now the reason I would up with this article is my client was actually adding custom data in addition to that name/slug, for instance an image. So if we added ‘red’ to one taxonomy, and then ‘red’ again to another and wanted different images, those two ‘red’s need to have different slugs. You can do this by hand of course, setting the slug to a new one each time, but it can be tough to keep track of, so I wanted to created an automated way, which is what I described above.
So I hope that clears things up for you; there’s no problem with sharing terms between taxonomies as long as the terms have the same meaning between them. If you need to have additional custom data added to the terms based on taxonomy, you need to create separate terms by using new slugs, and all my code above does is to simply that.
Believe me, you don’t want to use this when you have a professional site…having over 5k postings and using above will push your db on his knees.