— tomauger.com

Changing taxonomy slugs in WordPress – big ol’ gotcha

Okay, so this is a pretty edge case, I’ll admit it, but recently found myself having to do a manual swap of a taxonomy through direct manipulation of the database. Short story: we had used the default post category instead of a custom taxonomy, and needed to preserve all the existing terms, so we registered the new custom taxonomy and then went into the DB and did a quick UPDATE, setting the ‘taxonomy’ field of wp_term_taxonomy to the new slug. All appeared well.

Problems with hierarchical taxonomies

Strangely though, when we tried to do a tax query using get_terms( array( ‘parent’ => $parent_id ) ), we kept getting empty result sets. Checking the database that made no sense because the hierarchy was there, the parent_ids were set, all was good in $wpdb-land. But each query yielded no results, even with ‘hide_empty’ set to false so we got the entire list, rather than just the terms that were actually in use. Sigh.

After much digging through core, the culprit was _get_term_hierarchy() defined in wp-includes/taxonomy.php. This core function, marked as ‘private’ through the use of the underscore in front of its name, is responsible for drafting up an array where the keys are the term_ids of all the terms in the taxonomy, and the value is the parent_id of that term. It’s just a shortcut, really, and in my opinion is probably a bit redundant. EXCEPT, that it was decided to use a primitive form of cacheing here. This must predate the $wp_cache because that probably should have been used instead. What it does is it stores the resulting hierarchy within the wp_options table of the database, with the option_name of {$taxonomy}_children.

So the _get_term_hierarchy() function first checks to see whether such an option record exists. If it does, it just uses that and returns the result. If it does not, it performs a proper query on the DB and stores that in the wp_options record for future use, before returning the new result.

So here’s where things fell apart. I suppose this option gets updated every time you at a term using the admin interface. Clearly, if you short-circuit this by writing directly to the DB, you won’t be thinking of updating the serialized array in wp_options. And when you register the taxonomy for the first time, it does write that option, so it will be there from the get-go, an empty array waiting to be filled with happy children like Mother Hubbard’s shoe. And that short-circuits the _get_term_hierarchy() call, which is used whenever you specify ‘parent’ or ‘child_of’ in your args to get_terms().

The solution in this case was simply to delete the option from the database and then run the query again. This forces _get_term_hierarchy() to re-build the list and cache it in wp_options, and all is well with the world.

So remember this little gotcha whenever you’re manipulating a hierarchical taxonomy directly on the database – not that this should happen very often, but I could see it cropping up during an import of terms from another DB, or, as in my case, a switch of taxonomy name.

  • http://www.facebook.com/profile.php?id=20800487 Tyrone Warren

    Oh man, you RULE!  This really helped me out.

  • Wielsma

    Thanks man. I’m about to do a manual DB update of an existing taxonomy slug for multiple posts and this is just the heads-up I needed to prevent a small disaster from happening :)

  • jared rethman

    Could you be a little more specific on which option to delete in wp_options table?