I have a custom field called post_views_count which stores the views of each post on my blog. I would like to use these views to decide which category the posts belong. Here is what I would like to happen:

  1. Put the latest 50 posts into groups of 10.
  2. Find which post in each group has the highest views and move them to the large category.
  3. Find the average of the remaining 9 posts in each group.
  4. Posts which are below the average of each group are moved to the small category and posts above the average are moved to the medium category.

This should result in 1 large post, 4 or 5 medium and 4 or 5 small posts per group. This process would run on a cron every hour to refresh the categories as well as running each time a new post is made. When a post leaves the latest 50 it just remains the category it was last assigned.

I've looked around for similar solutions and I'm not able to find much. Any help would be much appreciated.

  • Do you absolutely need to create the 5 groups of 10 at first?
    – ClemC
    Aug 6, 2017 at 17:17
  • I mean what's the point of putting the posts in what you call groups in first place? What's a group? By which criteria are they defined?
    – ClemC
    Aug 6, 2017 at 17:21
  • The groups are because I don't want categories bunched up such as 3 "large" posts next to each other or 30 small posts in a row. Another way to think about it is take the 10 latest posts (1st group) and put them through the process I explained, then the next 10 (2nd group), then the next 10 (3rd) until the 50 latest have been done. Aug 6, 2017 at 17:29
  • I understand now, see my answer.
    – ClemC
    Aug 7, 2017 at 8:29

1 Answer 1


I took the freedom to ignore step #1 and step #3, as they seem being part of your primary approach to achieve your goal and not being your goal itself. I believe you can achieve your goal in a simpler way.

function assign_last_posts_cat_by_views( $posts_number = 50, $delete_last_posts_transient = true ) {
    if ( true === $delete_last_posts_transient ) {
        delete_transient( 'last_posts' );

    if ( ! $posts = get_transient( 'last_posts' ) ) {
        $args = array(
            'numberposts' => $posts_number

        $posts = get_posts( $args ); // Gets your posts...

        set_transient( 'last_posts', $posts, 60*60*24 ); // Caches the result...

    $post_views = array();

    foreach ( $posts as $post ) {
        $post_views[ $post->ID ] = get_post_meta( $post->ID, 'post_views_count', true ); // Gets each post views count...

    arsort( $post_views, SORT_NUMERIC ); // Sorts views in desc order...

    $post_number = 0;

    foreach ( $post_views as $post_id => $views ) {
        if ( $post_number < 5 ) {
            $term_slug = 'large';

        } elseif ( $post_number < 30 ) {
            $term_slug = 'medium';

        } else {
            $term_slug = 'small';

        $term = get_term_by( 'slug', $term_slug, 'category' ); // Gets term and caches it...

        wp_set_post_terms( $post_id, array( $term->term_id ), 'category' ); // Programatically assignes the category to the post...


Now, as CRON job, to refresh the categories of your last posts every hour without running a new post query, you can do something like this...

add_action( 'hourly_process', 'assign_last_posts_cat_by_views', 10, 2 );
wp_schedule_event( time(), 'hourly', 'hourly_process', array( 50, false ) );

And to run the process when a post is saved...

add_action( 'save_post', 'assign_last_posts_cat_by_views' );
  • It looks great but unfortunately on testing it seems to remove categories from all posts but not reassign them to new ones. I can't quite figure out why. Aug 7, 2017 at 12:44
  • @mastercoeurl, maybe the problem comes from $term which contains the name of the term. Looks like, wp_set_post_terms() second argument should contain an array of ID of the category term instead if we refer to the codex. See my updated answer and let me know if it works or not...
    – ClemC
    Aug 7, 2017 at 13:27
  • Glad it helped :) See my updated answer for last little code improvements.
    – ClemC
    Aug 7, 2017 at 13:57
  • Just one issue I am having is that when posts leave the latest 50, they default to the small category when a new post or the cron runs. Is it possible to make them remain the category they were assigned before leaving the latest 50? Aug 7, 2017 at 23:31
  • @mastercoeurl, normally, they should remain in their category since this code doesn't ever touch older posts than the 50 lasts...
    – ClemC
    Aug 8, 2017 at 7:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.