Working with “split terms” in WP 4.2+

Prior to WP 4.2, terms in different taxonomies with the same slug (for instance, a tag and a category sharing the slug “news”) shared a single term ID. Beginning in WordPress 4.2, when one of these shared terms is updated, it will be split: the updated term will be assigned a new term ID.

In the vast majority of situations, this update will be seamless and uneventful. However, some plugins and themes store term IDs in options, post meta, user meta, or elsewhere. WP 4.2 will include two different tools to help authors of these plugins and themes with the transition.

The 'split_shared_term' action The 'split_shared_term' action

When a shared term is assigned a new term ID, a new 'split_shared_term' action is fired. Plugins and themes that store term IDs should hook to this action to perform necessary migrations. The documentation for the hook is as follows:

/**
 * Fires after a previously shared taxonomy term is split into two separate terms.
 *
 * @since 4.2.0
 *
 * @param int $term_id ID of the formerly shared term.
 * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
 * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
 * @param string $taxonomy Taxonomy for the split term.
 */

Here are a few examples of how plugin and theme authors can leverage this action to ensure that stored term IDs are updated.

Updating a term ID stored in an option Updating a term ID stored in an option

Let’s say your plugin stores an option called ‘featured_tags’ that contains an array of term IDs (update_option( 'featured_tags', array( 4, 6, 10 ) )). In this example, you’ll hook to 'split_shared_term', check whether the updated term ID is in the array, and update if necessary.

/**
 * Update featured tags when a term gets split.
 *
 * @param int $term_id ID of the formerly shared term.
 * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
 * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
 * @param string $taxonomy Taxonomy for the split term.
 */
function my_featured_tags_split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
    // We only care about tags, so we'll first verify that the term is a tag.
    if ( 'post_tag' == $taxonomy ) {
        // Get the current featured tags.
        $featured_tags = get_option( 'featured_tags' );

        // If the updated term is in the array, note the array key.
        $found_term = array_search( $term_id, $featured_tags );
        if ( false !== $found_term ) {
            // The updated term is a featured tag! Replace it in the array, and resave.
            $featured_tags[ $found_term ] = $new_term_id;
            update_option( 'featured_tags', $featured_tags );
        }
    }
}
add_action( 'split_shared_term', 'my_featured_tags_split_shared_term', 10, 4 );

Top ↑

Updating a term ID stored in post meta Updating a term ID stored in post meta

Sometimes a plugin might store term ids in post meta. In this case, use a get_posts() query to locate posts with the meta key “primary_category” and a meta value matching the split term ID. Once you’ve identified the posts, use update_post_meta() to change the values stored in the database.

/**
 * Check primary categories when a term gets split to see if any of them
 * need to be updated.
 *
 * @param int $term_id ID of the formerly shared term.
 * @param int $new_term_id ID of the new term created for the $term_taxonomy_id.
 * @param int $term_taxonomy_id ID for the term_taxonomy row affected by the split.
 * @param string $taxonomy Taxonomy for the split term.
 */
function my_primary_category_split_shared_term( $term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
    // Ignore all updates except those to categories
    if ( 'category' == $taxonomy ) {
        // Find all the posts where the primary category matches the old term ID.
        $post_ids = get_posts( array(
            'fields' => 'ids',
            'meta_key' => 'primary_category',
            'meta_value' => $term_id,
        ) );

        // If we found posts, update the term ID stored in post meta.
        if ( $post_ids ) {
            foreach ( $post_ids as $post_id ) {
                update_post_meta( $post_id, 'primary_category', $new_term_id, $term_id );
            }
        }
    }
}
add_action( 'split_shared_term', 'my_primary_category_split_shared_term', 10, 4 );

Top ↑

The `wp_get_split_term()` function The `wp_get_split_term()` function

'split_shared_term' is the preferred method for processing term ID changes. However, there may be cases – such as a delayed plugin update – where terms are split, without your plugin having a chance to hook to the 'split_shared_term' action. WP 4.2 stores information about taxonomy terms that have been split, and provides the wp_get_split_term() utility function to help developers retrieve this information.

Consider the case above, where your plugin stores term IDs in an option called ‘featured_tags’. You may want to build a function that validates these tag IDs (perhaps to be run on plugin update), to be sure that none of the featured tags has been split:

function my_featured_tags_check_for_split_terms() {
    $featured_tag_ids = get_option( 'featured_tags', array() );

    // Check to see whether any IDs correspond to post_tag terms that have been split.
    foreach ( $featured_tag_ids as $index => $featured_tag_id ) {
        $new_term_id = wp_get_split_term( $featured_tag_id, 'post_tag' );

        if ( $new_term_id ) {
            $featured_tag_ids[ $index ] = $new_term_id;
        }
    }

    // Resave.
    update_option( 'featured_tags', $featured_tag_ids );
}

Note that wp_get_split_term() takes two parameters – $old_term_id and $taxonomy – and returns an integer. If you need to retrieve a list of all split terms associated with an old term ID, regardless of taxonomy, use wp_get_split_terms( $old_term_id ).