Search code examples
wordpresscustom-post-typecustom-taxonomybulktaxonomy-terms

Bulk update all taxonomy terms in wordpress


My custom post type "references" has a custom field called "references_count". It has a numeric value.

I have an custom taxonomy called "country" with a custom field called "country_count" for the terms.

Background: The custom post type "references" saves cities with a number of clients in this city. This value is saved in the field "references_count". In the custom taxonomy there are countries. For each country, there is a total number of references.

Example: In the city of "Berlin" there are 3 clients. In the city of "Munich" there are 2 clients. The taxonomy term "Germany" includes the sum of all cities in this country. So the value of "country_count" in this example for the taxonomy term "Germany" is 5, being the sum of the references of each city.

I wrote this code which is working, if I'm saving each individual taxonomy term.

add_action( 'edited_country', 'update_counter_for_countries', 10, 2 );
 
function update_counter_for_countries( $term_id ) {


// Get posts with term
$args = array(
    'post_type' => 'reference',
    'posts_per_page' => -1,
    'tax_query' => array(
        array(
        'taxonomy' => 'country',
        'field' => 'term_id',
        'terms' => $term_id
        )
    )
);
$the_query = new WP_Query( $args );

// sum values in posts
$sumTerm = 0;
if ( $the_query->have_posts() ) {
    while ( $the_query->have_posts() ) {
        $the_query->the_post();
        $number = get_field( 'references_count', get_the_ID() );
        $sumTerm = $sumTerm + $number;
    }
}
wp_reset_postdata();

// update field in term
update_field( 'country_count', $sumTerm, 'country'.'_'.$term_id );
}

Problem: I have more than 100 countries (taxonomy terms), so I have to save each term individually to get things going.

What I am looking for: Is there a way to update / save all custom taxonomy terms at once, so I don't have to update each term seperately? I checked out a lot of plugins, but couldn't find any plugin which gives the possibility of "bulk edit" or "bulk save" taxonomy terms. I would prefer a solution without plugin if possible. I am very grateful for any hint, thank you very much.


Solution

  • I wanted to have a neat admin page with a button to bulk update all my taxonomy terms. The process should work using AJAX so it can take a while without confusing the user. There should be status messages.

    So in the first step I added a new admin page to the wordpress backend.

    add_action( 'admin_menu', array( $this, 'my_admin_page' ) );
    
    function my_admin_page() {
        add_menu_page(
            __('Bulk Terms'), // page title
            __('Bulk Terms'), // menu title
            'manage_options', // user capabilities
            'options-page', // menu slug
            'my_output_function', // output function
            'dashicons-admin-generic', // menu icon
            77 // menu position
        );
    }
    

    In the output function I put a form with a button to bulk update terms. And some status messages.

    function my_output_function() {
        echo '<div class="wrap">';
            echo '<form action="admin.php?page=options-page" method="post">';
                wp_nonce_field( 'ajax_validation', 'nonce' ); // security feature
                submit_button('Update Terms', 'primary', 'submitOptions', false);
            echo '</form>';
            echo '<div id="processing" style="display:none;">Please wait</div>';
            echo '<div id="error" style="display:none;">Something went wrong</div>';
            echo '<div id="success" style="display:none;">Done!</div>';
        echo '</div>';
    }
    

    In the second step I had to enqueue script file for ajax calls:

    add_action( 'admin_enqueue_scripts', 'my_ajax_scripts' );
    
    function my_ajax_scripts() {
      // Check if on specific admin page
      global $pagenow;
      if (( $pagenow == 'admin.php' ) && ($_GET['page'] == 'options-page')):
        wp_enqueue_script( 'ajaxcalls', plugin_dir_url( __FILE__ ).'/js/ajax-calls.js', array('jquery'), '1.0.0', true );
    
        wp_localize_script( 'ajaxcalls', 'ajax_object', array(
          'ajaxurl' => admin_url( 'admin-ajax.php' ),
          'ajaxnonce' => wp_create_nonce( 'ajax_validation' )
        ) );
      endif;
    }
    

    And create the function to bulk update all my taxnomy terms:

    function options_page_action() {
        $taxonomy = "country";
        $terms = get_terms([
            'taxonomy' => $taxonomy,
            'hide_empty' => false,
        ]);
        foreach ($terms as $term) {
            $term_id = $term->term_id;
            // Get posts with term
            $args = array(
                'post_type' => 'reference',
                'posts_per_page' => -1,
                'tax_query' => array(
                    array(
                        'taxonomy' => 'country',
                        'field' => 'term_id',
                        'terms' => $term_id
                    )
                )
            );
            $the_query = new WP_Query( $args );
    
            // sum values in posts
            $sumTerm = 0;
            if ( $the_query->have_posts() ) {
                while ( $the_query->have_posts() ) {
                    $the_query->the_post();
                    $number = get_field( 'references_count', get_the_ID() );
                    $sumTerm = $sumTerm + $number;
                }
            }
            wp_reset_postdata();
            // update field in term
            update_field( 'country_count', $sumTerm, 'country'.'_'.$term_id );
        }
        $result = array( 'status' => 'success' ); // create response
        wp_send_json_success( $result ); // send response
        wp_die(); // close ajax request
    }
    

    As the third step, in my ajax_calls.js file I take the click event to call the function for updating the taxonomy terms using ajax.

    $( '#submitOptions' ).click( function(event) {
      event.preventDefault();
      $('#submitOptions').css('cssText','display:none;');
      $('#processing').css('cssText','display: block;');
      $.ajax({
        type: 'POST',
        url: ajax_object.ajaxurl,
        data: {
            action: 'options_page_action',
            nonce: ajax_object.ajaxnonce
        },
        success: function( response ) {
          if( response['data']['status'] == 'success' ) {
            $('#processing').css('cssText','display:none;');
            $('#success').css('cssText','display:block;');
          }
        },
        error: function() {
          $('#processing').css('cssText','display:none;');
          $('#error').css('cssText','display:block;');
        }
      });
    });
    

    There are messages indicating that the function is running and it show messages when it's done or has an error. This way the user will always know what's going on when bulk updating terms.