I have some author names held in two different taxonomies where the terms are formatted in two different but overlapping ways. There are about 400 terms in each taxonomy, associated with roughly 700 posts. (The reason for all this mess is a complicated migration from an old version of Expression Engine.)

The first taxonomy is just default tags and, after the merge, I'll delete all these terms. The format is this, without the quotes, where "123" could be any three or four digit numeral:

"[123] LastName, FirstName"

The other taxomomy, which I'll keep, is called author_tax. Its format is simpler:

"FirstName LastName"

Of course, I'd like to keep all the current post associations for both taxonomies. Best case scenarios is the terms are merged only if FirstName and LastName match. But even just matching LastName would save HOURS of manually merging. I might get some bad matches, but cleaning that up would be easier than a manual merge of 400 terms.

Is this possible or am I dreaming?

(By the way, these are not traditional WordPress authors who would create posts, but authors for a WP-driven magazine website. So none of this has anything to do with the user tables.)

  • Could you post some example posts/taxonomy/term relations, just so we can get our head around this. Mar 6, 2015 at 14:36
  • From group one ("tags"), inherited from the old site: [102] Kearney, Douglas | [103] Hardy, Myron | [104] Parker-Ohene, Cynthia
    – Josh M
    Mar 6, 2015 at 14:39
  • From group two (author_tax): Douglas Kearney | Myron Hardy | Cynthia Parker-Ohene
    – Josh M
    Mar 6, 2015 at 14:40

1 Answer 1


Grab all the post tags and loop over them. If it's deemed an "author" by a pattern match, check to see if the equivalent author_tax term exists. If not, create it.

Now you can build two stacks: one for author_tax terms to append to the post, and another for post_tag's to be removed.

$post_tags = get_the_terms( $post_id, 'post_tag' );
$authors =
$remove = array();

foreach ( $post_tags as $post_tag ) {
    if ( preg_match( '/^\[[0-9]+\] *([^,]+), *(.+)$/', $post_tag->name, $match ) ) {
        $fullname = "$match[2] $match[1]";

        if ( $term = get_term_by( 'name', $fullname, 'author_tax' ) ) // Already exists
            $authors[] = ( int ) $term->term_id;
        elseif ( ! is_wp_error( $term = wp_insert_term( $fullname, 'author_tax' ) ) ) // Create new author tax term
            $authors[] = ( int ) $term['term_id'];

        $remove[] = ( int ) $post_tag->term_id;

$authors && wp_set_object_terms( $post_id, $authors, 'author_tax', true /* Append terms */ );
$remove && wp_remove_object_terms( $post_id, $remove, 'post_tag' );

Either pop this in a foreach/while loop for all posts, or batch it up. With 700, you should be able to get away with a straight run so long as you can increase the timeout.

This code is not tested. Backup first!

Update: Since this is a one-time thing, just create a file import.php in your WordPress install folder, and then load WordPress in on the first line:

require './wp-load.php';
ini_set( 'max_execution_time', 300 );

$posts = get_posts( 'posts_per_page=-1&fields=ids' );
foreach ( $posts as $post_id ) {
    // Code from above

Hit that script from your browser & wait! I would run a test with just one post to start & check everything works.

  • Hey TheDeadMedic. I'm gonna try this out. If I do it as a loop, what template file would be best to put it in? As for "batch it up," I'm not sure how to do that at all. Could you explain? Thanks!
    – Josh M
    Mar 6, 2015 at 16:22
  • 1
    Check revision. Mar 6, 2015 at 16:26
  • I need to quickly go through the posts but I think you did it! Eventually I got this message 6 times: "Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\ecotone\import.php on line 12" Line 12 is foreach ( $post_tags as $post_tag ) { What do you think is going on with that?
    – Josh M
    Mar 6, 2015 at 16:43
  • I don't know what the deal with the error was but your solution is PERFECT. You have no idea how happy I am--and how awesome you are. Thanks!
    – Josh M
    Mar 6, 2015 at 16:48
  • 1
    The code doesn't check $post_tags is an array. If a post has no tags, it'll be false. Mar 6, 2015 at 17:19

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.