By default, your main WordPress blog page displays your most recent posts in descending date order. But if you're using categories on your site and your readers will want to see what's new in each category, you might want your blog page to look different.
In this tutorial I'll show you how to do just that. I'll demonstrate how to:
- identify all of the categories on your blog
- display the most recent post for each one, with a featured image if the post has one
- make sure that posts in more than one category don't get duplicated
- add some styling to make it look good
What You'll Need
To follow this tutorial, you'll need:
- A development installation of WordPress.
- Some posts and categories set up. I've used a sample of the data from the WordPress theme unit test data.
- A theme. I'm going to create a child theme of the Twenty Fourteen theme.
- A code editor.
Setting Up the Theme
The first step is to get the theme set up. I'm going to create a child theme of the Twenty Fourteen theme, with just two files: style.css
and index.php
.
Here's my stylesheet:
/* Theme Name: Display the Most Recent Post in Each Category Theme URI: http://code.tutsplus.com/tutorials/display-the-most-recent-post-in-each-category--cms-22677 Version: 1.0.0 Description: Theme to accompany tutorial on displaying the most recent post fort each term in a taxonomy for Tutsplus, at http://bitly.com/14cm0yb Author: Rachel McCollin Author URI: http://rachelmccollin.co.uk License: GPL-3.0+ License URI: http://www.gnu.org/licenses/gpl-3.0.html Domain Path: /lang Text Domain: tutsplus Template: twentyfourteen */ @import url('../twentyfourteen/style.css');
I'll come back to this file later to add styling, but for now it's all WordPress needs to recognize the child theme.
Creating the Index File
As I want my main blog page to display the latest post in each category, I'm going to create a new index.php
file in my child theme.
Creating an Empty index.php File
First I'll copy the index.php
file from Twenty Fourteen and edit out the loop and other content so it looks like this:
<?php /** * The main template file. * * Based on the `index.php` file from TwentyFourteen, with an edited version of the `content.php` include file from that theme included here. */ ?> <?php get_header(); ?> <div id="main-content" class="main-content"> <?php if ( is_front_page() && twentyfourteen_has_featured_posts() ) { // Include the featured content template. get_template_part( 'featured-content' ); } ?> <div id="primary" class="content-area"> <div id="content" class="site-content" role="main"> </div> </div> <?php get_sidebar( 'content' ); ?> </div> <?php get_sidebar(); ?> <?php get_footer(); ?>
Identifying the Categories
The first step is to identify the categories in your blog. Immediately after the opening <div id="content">
tag, add the following:
<?php $categories = get_categories(); foreach ( $categories as $category ) { } ?>
This uses the get_categories()
function to fetch a list of the categories in the blog. By default this will be fetched in alphabetical order and any empty categories won't be included. This works for me so I'm not adding any extra arguments.
Then I'm using foreach ( $categories as $category ) {}
to tell WordPress to run through each of these categories in turn and run the code inside the braces. The next step will be to create a query which will run for each of those categories.
Defining Query Arguments
Now you need to define the arguments for your query. Inside the braces, add this:
$args = array( 'cat' => $category->term_id, 'post_type' => 'post', 'posts_per_page' => '1', );
This will fetch just one post in the current category.
Running the Query
Next, insert your query, using the WP_Query
class:
$query = new WP_Query( $args ); if ( $query->have_posts() ) { ?> <section class="<?php echo $category->name; ?> listing"> <h2>Latest in <?php echo $category->name; ?>:</h2> <?php while ( $query->have_posts() ) { $query->the_post(); ?> <article id="post-<?php the_ID(); ?>" <?php post_class( 'category-listing' ); ?>> <?php if ( has_post_thumbnail() ) { ?> <a href="<?php the_permalink(); ?>"> <?php the_post_thumbnail( 'thumbnail' ); ?> </a> <?php } ?> <h3 class="entry-title"> <a href="<?php the_permalink(); ?>"> <?php the_title(); ?> </a> </h3> <?php the_excerpt( __( 'Continue Reading <span class="meta-nav">→</span>', 'twentyfourteen' ) ); ?> </article> <?php } // end while ?> </section> <?php } // end if // Use reset to restore original query. wp_reset_postdata();
This will output the featured image, title and excerpt for each post, with each enclosed in a link.
Let's take a look at how this looks now:
As you can see, there's a problem. My page is displaying the most recent post in each category, but it's duplicating posts because sometimes a post will be the most recent post in more than one category. Let's fix that.
Avoiding Duplication of Posts
Above the line where you added the get_categories()
function, add this line:
$do_not_duplicate = array();
This creates an empty array called $do_not_duplicate
, which we'll use to store the ID of each post as it's output and then to check if the ID of any post being queried later on is in that array.
Next, add a new line below the opting of your query, so the first two lines look like this:
<?php while ( $query->have_posts() ) { $query->the_post(); $do_not_duplicate[] = $post->ID; ?>
This adds the ID of the current post to the $do_not_duplicate
array.
Finally, add a new argument to your query arguments to avoid outputting any posts in this array. Your arguments will now look like this:
$args = array( 'cat' => $category->term_id, 'post_type' => 'post', 'posts_per_page' => '1', 'post__not_in' => $do_not_duplicate );
This uses the 'post__not_in'
argument which looks for an array of post IDs.
Save your index.php
file and take a look at your blog page again:
That's better! Now your posts aren't duplicated.
Adding Styling
At the moment it's all a bit spread out, with the featured images above the post title and excerpt. Let's add some styling to float the image to the left.
In your theme's style.css
file, add the following:
.listing h2 { margin-left: 10px; } .category-listing img { float: left; margin: 10px 2%; } .category-listing .entry-title { clear: none; }
Now the content fits more nicely onto the page and is laid out better:
Adapting This Technique to Different Content Types
You could adapt this technique to work with different content types or taxonomies. For example:
- If you wanted to use a custom taxonomy term in place of categories, you would replace
get_categories()
withget_terms()
and change the'cat'
query argument to one looking for taxonomy terms. - If you're working with a different post type, you would add similar code to the template file which will display that post type, replacing the
'post_type' => 'post'
argument in your query arguments with your post type. - If you wanted to create a separate page from your main blog page to display the latest post of any post type for a given taxonomy, you would create a taxonomy archive template and add an adapted version of this code to that.
- You could take things further and use this technique with multiple taxonomies or multiple post types, using nested
foreach
statements to run multiple loops. - You could take the code above and add it to your
single.php
page to display a link to the latest post in each category after the content of the post. If you do this, you'll need to add the ID of the current page being displayed to the$do_not_duplicate
array.
Summary
It can sometimes be helpful to display the latest posts on your blog in another way than simply in chronological order. Here I've demonstrated a technique for showing the latest post in each of the categories on your blog, ensuring that posts don't get duplicated if they're in multiple categories.
Comments