How to bulk-update post categories in WordPress

When using WordPress it is often the case that posts need their assigned category changed. This can pose some issues when working with thousands of posts, or when importing posts from another website with different category names.

WordPress does feature a bulk-updating feature (by selecting a section of posts in the CMS and editing), however this isn’t always ideal. The process can time out when dealing with a large number of posts, as well as each operation being limited by the pagination in WordPress (999 post maximum). It also does not remove categories already assigned to a post, instead it will only add new categories. This simple code snippet solves these issues.

Before doing this, ensure you have made a database backup.

To use the script, create a new WordPress template file in your theme folder:

/wp-content/themes/theme-name/templates/tpl-update-categories.php:

The script makes use of the WordPress function get_posts() to create an array of all matching posts. There are many options in the documentation you can use to easily filter the posts to only get your desired selection.

For this example, I will move all posts currently assigned to the “Public” and “Trade” categories, and move them to the “News” category only. My parameters for get_posts() will find an unlimited number (-1 does this) of published posts in the both categories (using the category IDs):

<?php // Template Name: Update categories

// Get all posts
$args = array (
    'post_status' => 'publish',
    'numberposts' => -1,
    'category'    => array(4, 5)
);
$posts = get_posts($args);

Go ahead and create a new Page in WordPress. On the right sidebar, select the new template “Update categories” and click “Publish”.

When this is done, go to the page which is now using the code found in the template file. Using print_r and viewing the page-source (easier to read) will show the array with all posts found matching the parameters previously set:

print_r($posts);

A section of the full result (20 articles found) can be seen below:

Array
(
    [0] => WP_Post Object
        (
            [ID] => 25
            [post_author] => 1
            [post_date] => 2018-01-23 16:41:07
            [post_date_gmt] => 2018-01-23 16:41:07
            [post_content] => 
            [post_title] => Article 20
            [post_excerpt] => 
            [post_status] => publish
            [comment_status] => open
            [ping_status] => open
            [post_password] => 
            [post_name] => article-20
            [to_ping] => 
            [pinged] => 
            [post_modified] => 2018-01-23 16:55:40
            [post_modified_gmt] => 2018-01-23 16:55:40
            [post_content_filtered] => 
            [post_parent] => 0
            [guid] => http://wordpress-testing.traffick/?p=25
            [menu_order] => 0
            [post_type] => post
            [post_mime_type] => 
            [comment_count] => 0
            [filter] => raw
        )

    [1] => WP_Post Object
        (
            [ID] => 24
            [post_author] => 1
            [post_date] => 2018-01-23 16:40:59
            [post_date_gmt] => 2018-01-23 16:40:59
            [post_content] => 
            [post_title] => Article 19
            [post_excerpt] => 
            [post_status] => publish
            [comment_status] => open
            [ping_status] => open
            [post_password] => 
            [post_name] => article-19
            [to_ping] => 
            [pinged] => 
            [post_modified] => 2018-01-23 16:55:42
            [post_modified_gmt] => 2018-01-23 16:55:42
            [post_content_filtered] => 
            [post_parent] => 0
            [guid] => http://wordpress-testing.traffick/?p=24
            [menu_order] => 0
            [post_type] => post
            [post_mime_type] => 
            [comment_count] => 0
            [filter] => raw
        )
        
        ...

)

Go through the array manually, making sure there are no unwanted posts showing. If there are, you will need to apply furthering filtering in the get_posts() function.

If all is looking good, comment out or remove the “print_r($posts)” line. Now it’s time to loop through each individual post, check if it matches our conditions, then assign it to a new category if it does.

The categories for each post are to be updated using the wp_set_post_categories() function. get_cat_name() is also used to display the category name rather than an ID for better readability.

// Loop through all posts found
foreach ($posts as $post) {

    // More validation to ensure only desired posts are changed:
    if (in_category(array(4, 5))) {

        // Get details for each matching post:
        $id = $post->ID;
        $title = $post->post_title;

        // Update category for each post and get updated category name:
        $cat_update = wp_set_post_categories($id, array(2));
        $cat_update = get_cat_name($cat_update[0]);

        // Display results of update:
        echo $cat_update ? $title . ': category updated to ' . $cat_update . '<br/>' : NULL;

    }

} ?>

Refresh the page to run the code, and you should be presented with something like this:

Result of category updates

Job done! You can go back to your posts in WordPress and verify that all posts have been assigned to the correct category. Remember to delete the template file when you are finished with it.

See below for the full version of the script:

<?php // Template Name: Update categories

// Get all posts
$args = array (
    'post_status' => 'publish',
    'numberposts' => -1,
    'category'    => array(4, 5)
);
$posts = get_posts($args);

// print_r($posts);

foreach ($posts as $post) {

    // More validation to ensure only desired posts are changed:
    if (in_category(array(4, 5))) {

        // Get details for each matching post:
        $id = $post->ID;
        $title = $post->post_title;

        // Update category for each post and get updated category name:
        $cat_update = wp_set_post_categories($id, array(2));
        $cat_update = get_cat_name($cat_update[0]);

        // Display results of update:
        echo $cat_update ? $title . ': category updated to ' . $cat_update . '<br/>' : NULL;

    }

} ?>

Posted by Steven Cotterill - 27/05/2018

Want to work with us on a project? Get in touch!