I doubt it would justify the effort of writing custom SQL queries for only 2500 posts.
Within the wp_set_object_terms( $object_id, ... )
function we have:
$object_id = (int) $object_id;
so it’s correct that it only takes a single post id as input.
So you would need to loop over your $post_ids
array but it might be sufficient to use the wp_defer_term_counting()
trick1):
/**
* Add terms to posts (with debug display)
*/
function wpse_terms_to_posts( $terms, $taxonomy, $append, &$post_ids )
{
wp_defer_term_counting( true );
print '<ul>';
foreach( (array) $post_ids as $post_id )
{
$tt_ids = wp_set_object_terms( $post_id, $terms, $taxonomy, $append );
print '<li>';
if( is_wp_error( $tt_ids ) )
printf(
'Problem adding terms to post_id: %d - Error: %s',
$post_id,
$tt_ids->get_error_message()
);
else
print 'Success adding terms to post_id: ' . $post_id ;
print '</li>';
}
print '</ul>';
wp_defer_term_counting( false );
}
Here we pass $post_ids
by reference in case it’s very large.
Usage (PHP 5.4+) :
wpse_terms_to_posts(
$terms = [ 6, 8 ],
$taxonomy = 'category',
$append = true,
$post_ids = [ 123, 234, 345 ]
);
We could even slice the $post_ids
array, if 2500 is too much for our system in a single run.
Here’s an example how we can run the function with admin-ajax.php
with such a slicing feature:
We create our own action with:
/**
* Custom wp ajax action to activate our wpse_terms_to_posts() function
*
* Example: /wp-admin/admin-ajax.php
* ?action=wpse-terms-to-posts&wpse_offset=0&wpse_length=500
*/
add_action( 'wp_ajax_wpse-terms-to-posts', function()
{
$offset = filter_input( INPUT_GET, 'wpse_offset', FILTER_SANITIZE_NUMBER_INT );
$length = filter_input( INPUT_GET, 'wpse_length', FILTER_SANITIZE_NUMBER_INT );
// Check for user capability and non empty user input:
if( current_user_can( 'manage_options' ) && '' !== $offset && '' !== $length )
{
print '<h3>Start:</h3>';
$start = microtime( true );
// Settings - Edit to your needs:
$terms = [ 6, 8 ];
$taxonomy = 'category';
$append = true;
$post_ids = [ 123, 234, 345, 456, 567, 678, 789 ];
// Add terms to posts:
wpse_terms_to_posts (
$terms,
$taxonomy,
$append,
array_slice( $post_ids, $offset, $length )
);
printf( '<h3>Done in %f seconds</h3>', microtime( true ) - $start );
exit();
}
} );
where we use the wpse_offset
and wpse_length
GET parameters to slice the posts array.
If we want to slice the post ids array into chunks of 500, we can activate it manually with:
/wp-admin/admin-ajax.php?action=wpse-terms-to-posts&wpse_offset=0&wpse_length=500
/wp-admin/admin-ajax.php?action=wpse-terms-to-posts&wpse_offset=500&wpse_length=500
/wp-admin/admin-ajax.php?action=wpse-terms-to-posts&wpse_offset=1000&wpse_length=500
/wp-admin/admin-ajax.php?action=wpse-terms-to-posts&wpse_offset=1500&wpse_length=500
/wp-admin/admin-ajax.php?action=wpse-terms-to-posts&wpse_offset=2000&wpse_length=500
That way we can check for any term errors.
Hope you can modify this to your needs.
1) wp_defer_term_counting()
was first mentioned on WPSE in this answer by @JanFabry