After reading several posts regarding post category manipulation with a given time I wanted to create a plugin that would manipulate posts after a date. My original post reference is from six years ago: “Plugin for changing a post’s category based on it’s post date?” but I’ve referenced several posts regarding manipulation:

  • I need to bulk update all wordpress posts on a scheduled time
  • How can I hide posts that are over 2 years old

However it isn’t removing the category and adding the new one:

Code:

function check_if_cat_has_reached_time() {
    // test if term exists and if doesn't return
    $test_term1 = term_exists('Uncategorized', 'category');
    if ($test_term1 !== 0 && $test_term1 !== null) :
        // $default_cat = get_term_by('id', 'Uncategorized', 'category');
        $default_cat = get_cat_ID('uncategorized');
    else :
        return;
    endif;

    // test if term exists and if doesn't return
    $test_term2 = term_exists('failed', 'category');
    if ($test_term2 !== 0 && $test_term2 !== null) :
        $new_cat = get_cat_ID('failed');
    else :
        return;
    endif;

    global $post;

    // the Post ID
    $the_ID = $post->ID;

    // Post creation time in epoch
    // $post_creation   = get_the_time('U',$the_ID);
    // current time in epoch
    $current_time = time();

    // two years in epoch
    $two_years = 31556926*2;

    // post time plus two years
    $post_plus_two = (int)$two_years + (int)get_the_time('U',$the_ID);

    if ($current_time >= $post_plus_two && has_category($default_cat,$the_ID)) :
        wp_remove_object_terms($the_ID, $default_cat, 'category');
        $update_post_cat = array(
            'post_category' => array($new_cat),
        );
        wp_insert_post($update_post_cat);
    endif;
}
add_action('save_post', 'check_if_cat_has_reached_time');

Is there a reason why wp_remove_object_terms() doesn’t work? I used this after reading Remove specific category from a post.

So my questions are:

  • Am I removing the category correctly?
  • Am I updating the category correctly?
  • Is there better hook for manipulating posts in a plugin?
  • Would there be any performance issues if this was implemented on a site that had several thousand posts?

EDIT:

The below answer I’ve tried to implement in my theme’s function.php but it still doesn’t work. I’m using a default theme with WP Test. Before the function I make sure to create the categories with:

function create_foo_category() {
    wp_insert_term(
        'Foo',
        'category',
        array(
            'description'   => 'This is the Foo',
            'slug'          => 'foo'
        )
    );
}
add_action('after_setup_theme', 'create_foo_category');

then for bar:

function create_bar_category() {
    wp_insert_term(
        'Bar',
        'category',
        array(
            'description'   => 'This is the bar',
            'slug'          => 'bar'
        )
    );
}
add_action('after_setup_theme', 'create_bar_category');

I tried to manipulate the provided answer with:

function check_if_cat_has_reached_time() {
    global $post; 

    $the_ID = $post->ID;

    if (!wp_is_post_revision($the_ID)) {

    // unhook this function so it doesn't loop infinitely
    remove_action('save_post', 'check_if_cat_has_reached_time');

    // test if term exists and if doesn't return
    $check_default_cat = term_exists('Foo', 'category');
    if ($check_default_cat !== 0 && $check_default_cat !== null) {
        $default_cat = get_cat_ID('foo');
    } else {
        return;
    }

    // test if term exists and if doesn't return
    $check_new_cat = term_exists('Bar', 'category');
    if ($check_new_cat !== 0 && $check_new_cat !== null) {
        $new_cat = get_cat_ID('bar');
    } else {
        return;
    }

    // current time in epoch
    $current_time = time();

    // two years in epoch
    $two_years = 31556926*2;

    // post time plus two years
    $post_plus_two = (int)$two_years + (int)get_the_time('U',$the_ID);

    if ($current_time >= $post_plus_two && has_category($default_cat,$the_ID)) {
        wp_remove_object_terms($the_ID, $default_cat, 'category');
        $args = array(
            'ID'            => $the_ID,
            'post_category' => array($new_cat),
        );
        wp_update_post($args);
    }

    // re-hook this function
    add_action('save_post', 'check_if_cat_has_reached_time');
    }
}
add_action('publish_post', 'check_if_cat_has_reached_time');

EDIT:

After talking to a few people I think it was somewhat unclear what the intended purpose of the above was for. I’m looking to modify the posts without actually going into them with something like an event daily. After discussing this issue further I was made aware of wp_schedule_event() that I’m going to test out and edit.

2 Answers
2

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 Reply

Your email address will not be published. Required fields are marked *