I'm looking for a function that restricts the creation of each term to only 1 instance within each custom taxonomy. Each custom taxonomy may have the same term but the term may only occur once in each different custom taxonomy. I'm guessing this will need a function for each custom taxonomy.
1 Answer
Quite an interesting question. I cannot really find anything easy. Term creation is handled by the wp_insert_term()
function. If you look at the source code, you'll see that there really is not much filters or actions we can utilize before term insertion. pre_insert_term
is about the only filter that happens before term validation and insertion.
By default, in any hierarchical taxonomy, you can have terms with duplicate names if those terms are not within the same hierarchy. There is no filter or action to alter this behavior, so this will be our challenge to stop this behavior and make sure that we can only have one instance of a term.
Non-hierarchical taxonomies, because there can only be top level terms, will not have this issue. There can not be any other term with the same name.
There is however one exception to the rules above, whenever we explicitly set an unique slug, we can have terms with the same name on the same level. We will however skip this all together and just focus solely on the term name supplied
So we really are left with only the pre_insert_term
filter. Lets look at a possible solution
add_filter( 'pre_insert_term', function ( $term, $taxonomy )
{
/**
* Start by validating the term. The taxonomy is already validated
*
* If the term is a numeric or int value, we will bail. We will let wp_insert_term
* handle the process from here on. This is just a very loose check on our side
*/
if ( is_numeric( $term ) )
return $term;
/**
* $term is a valid non numeric string, so we most probably have a term name
*
* We will now basically use the same logic as `wp_insert_term` to validate the $term
* name.
*/
$term_name = strtolower( filter_var( $term, FILTER_SANITIZE_STRING ) );
/**
* Get all terms which matchings names like $term_name
*
* Getting terms by name is not an exact match, but a LIKE comparison, so
* if you have names like 'Term A', 'term a' and 'term A', they all will match.
* We will need to do an exact match later on
*/
$name_matches = get_terms(
$taxonomy,
[
'name' => $term_name,
'hide_empty' => false,
'fields' => 'names', // Only get term names
]
);
// If $name_matches is empty, we do not have duplicates, bail
if ( !$name_matches )
return $term;
// Convert all names into lowercase
$names_array = array_map( 'strtolower', $name_matches );
// Test to see if we have an exact term name match, if so, return WP_Error
if ( in_array( $term_name, $names_array ) )
return new WP_Error(
'term_exists',
__( 'You cannot have a term with the same name, choose a unique name.' ),
$term
);
// OK we have a unique term name, let 'wp_insert_term' continue the rest of the process
return $term;
}, 10, 2 );
What we have done is to get all terms with names like the term we would like to insert, convert them all to lower case, and then test if our term name is in the array of term names. If so, we will return a WP_Error
object in order to stop the whole term insertion process
Obviously, you do not need any other checks between taxonomies as you can have a term with the same name between different taxonomies.
-
1Great (unique) contribution! Clever to convert to lowercase before checking. If you'd like to see this implemented you're welcome to PM me :)– PeteCommented Mar 6, 2016 at 19:59
-
Term A
, that is the term's name. I can haveTerm A
istax_1
andtax_2
, but cannot haveTerm A
twice intax_1
.wp_insert_term()