WordPress: Display One Post Per Category

WordPress: pre_get_posts filter to display one post per category. Displays the latest sticky post and therefore excludes the category of the sticky post as we only want one post per category.
By: Silvan Hagen

/**
 * Main Query for the Front Page
 *
 * Display the latest post per category. The latest sticky post goes to the
 * top. It's category is then excluded as we only want 1 post per category.
 *
 * @param  object $query WP_Query
 * @return void          Uses $query->set to alter query vars
 */
function required_pre_get_posts_front_page( $query ) {
 
    // Check that we have the right query
    if ( ( $query->is_front_page() || $query->is_home() ) && $query->is_main_query() ) {
 
        // Set up the category ids and post ids arrays
        $category_ids = array();
        $post_ids = array();
 
        // Get the sticky_posts array
        $sticky_posts = get_option( 'sticky_posts' );
 
        // Check if we have sticky posts
        if ( is_array( $sticky_posts ) ) {
            // Give me the latest first
            rsort( $sticky_posts );
            // Only 1 sticky post needed
            $sticky_post = array_shift( $sticky_posts );
            // Get the cateogry of the sticky post
            $sticky_post_categories = wp_get_post_categories( $sticky_post );
        }
 
        // Set up category args
        $category_args = array( 'parent' => 0 );
 
        // Exclude cateogry of the sticky post
        if ( isset( $sticky_post_categories ) ) {
            $category_args['exclude'] = join( ',', $sticky_post_categories );
        }
 
        // Get parent categories
        $categories = get_categories( $category_args );
 
        // Get category term_ids
        foreach ( $categories as $category ) {
            $category_ids[] = $category->term_id;
        }
 
        // Get post ID of latest post per category.
        foreach ( $category_ids as $category_id ) {
            if ( $cat_posts = get_posts( array( 'cat' => $category_id, 'posts_per_page' => 1 ) ) ) {
                $first = array_shift( $cat_posts );
                $post_ids[] = $first->ID;
            }
        }
 
        // Add the sticky post to the front of the array
        if ( $sticky_post ) {
            array_unshift( $post_ids, $sticky_post );
        }
 
        // Set the additional query vars in the main Query.
        $query->set( 'post__in', $post_ids );
        // We handle sticky posts already
        $query->set( 'ignore_sticky_posts', 1 );
        // Preserver post__in array order
        $query->set( 'orderby', 'post__in' );
 
    }
    return;
 
}
add_action( 'pre_get_posts', 'required_pre_get_posts_front_page' );

Leave a Reply

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