Manipulate post category after time

Your current function works pretty well, but it’s hooked on to a post update hook so you need to actually edit/update a post for it to run. If you want the posts to be modified automatically after the specified time then you’ll need to set up a scheduled event. I can think of two ways to set up your schedule…

1. A recurring event that checks all (relevant) posts

We can use wp_schedule_event() to run at a hook at specified intervals:

// a custom hook to schedule
add_action( 'wpse_269529_check_posts', 'wpse_269529_check_posts_cats' );

// make sure the event hasn't been scheduled
if( !wp_next_scheduled( 'wpse_269529_check_posts' ) ) {

    // Schedule the event
    wp_schedule_event( time(), 'daily', 'wpse_269529_check_posts' );
}

The paramaters for wp_schedule_event() are (in order); when the first event should run (we can just pass time() to run it from now), how often they should run (one of either “hourly”, “twicedaily” or “daily”), and the hook to run. You can also pass some arguments but we don’t need that.

We also use wp_next_scheduled() to check that the event hasn’t already been scheduled.

Then we need the function that runs on that hook. We can use that to loop through all the posts with the category you want to replace, then update those with the new category:

function wpse_269529_check_posts_cats() {

    //categories
    $old_cat = get_cat_ID( 'foo' );
    $new_cat = get_cat_ID( 'bar' );

    // get all posts with the required category
    $args = array( 'posts_per_page' => -1, 'category' => $old_cat );
    $myposts = get_posts( $args );

    // loop through all posts and update with new category
    foreach ( $myposts as $mypost ) {
        $args = array(
            'ID'            => $mypost->ID,
            'post_category' => array( $new_cat ),
        );
        wp_update_post($args);
    }
}

You mention performance, and this runs a loop through all posts with the category, so maybe it’s not the best option…

2. A single scheduled event per post

You can do that with wp_schedule_single_event(), and create the schedule on post creation (note that’ll only work on new posts and not existing ones though). You hook a function to publish_post that’ll set the schedule:

// runs when a post is published
add_action( 'publish_post', 'wpse_269529_schedule_post_check' );

function wpse_269529_schedule_post_check( $post_id ) {

    // set the time when the event should be scheduled
    $timestamp = strtotime( '+2 years' );

    // Schedule the event
    wp_schedule_single_event( $timestamp, 'wpse_269529_check_post', array( $post_id ) );    
}

Now, because we hooked that on to publish_post, we can pass the post ID to the scheduled event and update that post based on that (remember to set the number of parameters on the action hook to “1” for our ID):

// a custom hook to schedule
add_action( 'wpse_269529_check_post', 'wpse_269529_check_post_cats', 10, 1 );

// replace post categories
function wpse_269529_check_post_cats( $post_id ) {

    //categories
    $old_cat = get_cat_ID( 'foo' );
    $new_cat = get_cat_ID( 'bar' );

    // check for the old category
    if ( has_category( $old_cat, $post_id ) ) {
        // update post with new category
        $args = array(
            'ID'            => $post_id,
            'post_category' => array( $new_cat ),
        );
        wp_update_post($args);
    }
}

You could use a combination of the two and loop through all existing posts on plugin or theme activation (or something else depending on when, why and how you’re doing this), then schedule an update per post for all new posts.

Leave a Comment