I have a situation where i have about 5000 imported records in a custom post type (travel) with a taxonomy category (country). Since i import from several different locations and each source seems to use a different description for some countries i want to update the term reference of that post to a uniform description.

So say the taxonomy 'country' has, amongst others, these values:

| term_id | name        |
|  1248   | Zuid Afrika |
|  3845   | zuid-afrika |

I want to update the term reference to 'country' that has a duplicate in each 'travel' post to a new defined id.

$terms = array(
    '3845' => '1248'
$args = array(
    'posts_per_page'    => -1,
    'post_type'         => 'travel',
    'post_status'       => 'publish'
global $post;
$my_query = new WP_Query($args);
    while ($my_query->have_posts()) : $my_query->the_post();
            This is the part where i get lost, what i want is something like this, but this doesn't work
            update term reference with value $terms[$post->term_id];

Any help would be really appreciated because i'm pretty much stuck right now

Edit: I don't want to delete the taxonomy categories because this scripts will run after the imports so if the duplicates are deleted they will be imported straight back but with a different term id

  • Have you considered to clean up the import data before the import instead of fixing it afterwards? Or at least fix the data during the import. How are you importing anyway? Via XML? directly to the database? A plugin? – kraftner Aug 22 '15 at 13:52
  • Apparently, the data is already imported, so it's too late for filtering now. – Ciprian Aug 24 '15 at 9:13

Unfortunately webtoure's solution didn't work so i decided to dive into Wordpress's database structure and came up with the following working code:

global $wpdb;
$taxonomy = 'country';
$environment = 0;

function _get_term_taxonomy_id($term_id){
    global $wpdb;
    $results = $wpdb->get_results("
        SELECT tt.term_taxonomy_id
        FROM wp_term_taxonomy AS tt
        LEFT JOIN wp_terms AS t ON t.term_id = tt.term_id
        WHERE t.term_id = '$term_id'
    foreach($results as $result){
        return $result->term_taxonomy_id;

/* MERGE */
foreach($merge_map as $from => $to){
    $from_id = _get_term_taxonomy_id($from);
    $to_id = _get_term_taxonomy_id($to);
    if($environment == 1){
                'term_taxonomy_id' => $to_id
            array( 'term_taxonomy_id' => $from_id )
        echo 'UPDATE '.$from_id.' with '.$to_id.' DONE

foreach($parent_map as $term_id => $parent_id){
    if($environment == 1){
                'parent' => $parent_id
            array( 'term_id' => $term_id )
        echo 'UPDATE parent of '.$term_id.' with '.$parent_id.' DONE
  • Sorry to hear that. My solution was specific to how you presented the question (using a posts loop), what didn't work in the end? (Why are you using that _get_term_taxonomy_id function and not get_term? What would happen if the term is associated with more taxonomies?) – webtoure Aug 25 '15 at 14:59
  • To be honest: i have no idea :) I'm not that deep into Wordpress and at the moment i'm quit happy it finally works. I don't know why it didn't work, sorry. Thanks for the help anyway!! Really appreciate it – Maurice Aug 26 '15 at 17:59
  • I meant how did it manifest? Anyway, there was a possibility that you would run into a timeout due to how long it takes to iterate over each post and execute the required operations. This approach might have worked on a paginated setup even though you might have ended up with a lot of pages to navigate through. :) – webtoure Aug 26 '15 at 18:06

If I'm understanding your requirement correctly, then the least painful way to do this is with the Term Management Tools plugin. You can reorganize your terms in the admin interface once imported.

EDIT: turns out I didn't understand your requirements correctly :)

  • This was an interesting journey into WP's source code. :) The thing is, OP's "Edit:" mentions not wanting to delete the terms which the plugin's merge function ultimately does by calling wp_delete_term. I don't quite understand what he meant by "[...] will be imported straight back [...]" but otherwise this is the best solution (perhaps slower though because it ultimately does a delete and insert instead of an update -- see the wp_set_object_terms function). – webtoure Aug 18 '15 at 20:24
  • I know that plugin and came (perhaps to the wrong) conclusion that i can't use it because i need to update the term links after each data import. The data imports will run hourly via cron and if the terms are deleted the data import will add the terms again, but then with (probably) a new term ID so i need to configure the term management tool after each import (which is not workable; 5000 terms, each hour ;)) – Maurice Aug 19 '15 at 11:15

There are quite a few steps involved here and also, with that 5000 posts figure you are mentioning, this will be an expensive task to do it through WordPress itself. However, here is what I came up with (I haven't tested this so you may wish to use it on a backup first and see if it works):

global $wpdb;

/* The ( 'from_term_id', 'to_term_id' ) map. */
$terms_map = array(
    '3845' => '1248',
    /* ... The rest of your mapping */

$taxonomy = 'country';
$obj_terms = wp_get_object_terms( $post->ID, $taxonomy );

/* Loop through each object term */
foreach ( $obj_terms as $term ) {
    /* See if the obj term_id is a key in $terms_map */
    if ( isset( $terms_map[$term->term_id] ) ) {
        /* We have a valid key. We now need the term_taxonomy_ids */
        /* for both 'from_term_id' and 'to_term_id' */
        $to_term = get_term( $terms_map[$term->term_id], $taxonomy );

        $from_term_tax_id = $term->term_taxonomy_id;
        $to_term_tax_id = $to_term->term_taxonomy_id;

        /* Update the '{prefix}_term_relationships' table */
        $update_res = $wpdb->update( 
            $wpdb->term_relationships, /* The table to update */
            array( 'term_taxonomy_id' => $to_term_tax_id ), /* Data to be updated */
            array( 'object_id' => $post->ID, 'term_taxonomy_id' => $from_term_tax_id ), /* Where clause */
            array( '%d' ), /* Format of the data is int */
            array( '%d', '%d' ) /* Format of the where clause is int */

        /* Finally, you may wish to update the term count for each term */
        wp_update_term_count( array( $from_term_tax_id, $to_term_tax_id ), $taxonomy );

Like I've mentioned though this may prove to be too expensive in which case you will have to write a separate PHP script and work directly on the database.

  • I'm going to try this one out next week. So far thanks for the suggestion! – Maurice Aug 19 '15 at 11:15
  • Hmm i tried the code but it doesn't work. I guess the reason is that i run the code on a custom page in my theme without any other code in it so $post->ID doesn't work (it least that's what i assume, i'm not that deep in wordpress code) Edit: Ignore my reaction, i didn't place the code inside the loop. Thnx! – Maurice Aug 25 '15 at 7:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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