2

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.

3
  • Interesting question. What have you tried, where have you failed. Just to be clear, I have a term called Term A, that is the term's name. I can have Term A is tax_1 and tax_2, but cannot have Term A twice in tax_1. Commented Mar 6, 2016 at 3:38
  • I've looked everywhere including this site (lots of answers to limit the displaying of terms but none regarding limiting the creation of terms). I'm guessing I might need to edit my register_taxonomy() function with a custom query variable or custom capabilities maybe? Your term/tax clarification is correct.
    – Pete
    Commented Mar 6, 2016 at 8:08
  • I do not have time to code anything now, but you might want to look into the filters inside wp_insert_term() Commented Mar 6, 2016 at 8:32

1 Answer 1

1

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.

2
  • 1
    Great (unique) contribution! Clever to convert to lowercase before checking. If you'd like to see this implemented you're welcome to PM me :)
    – Pete
    Commented Mar 6, 2016 at 19:59
  • My pleasure, glad it worked as indented. Enjoy ;-) Commented Mar 7, 2016 at 4:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.