Display the Most Recent Post in Each Category

In Portfolio+ I use a custom post type for portfolio items and a custom taxonomy for portfolio categories. When someone views a portfolio archive page all of the portfolio featured images are displayed, and when someone goes to a portfolio category all the featured images of posts in that category are displayed.

But a common request I’ve had is for a template that displays all the portfolio categories with thumbnail images for each of the categories. This would allow someone to easily link to different sections of their overall portfolio (for instance, “Photography”, “Water Color”, “Sculpture”) rather than having them all show up in one jumble of the main portfolio archive template.

This gets a bit tricky because there is no easy way to associate an image with a taxonomy term (though Michael Field’s Taxonomy Images Plugin has made it work).

I decided the best route would be to just display all of the categories, and use the featured image from the most recent post in each category. This obviously won’t be the perfect solution for everyone (some may want to use a completely different image for the category thumbnail), but for most users I think it works well and is a dynamic way to show off new work.

You can tweak all the code below to also work for regular posts and categories (or even other custom posts and taxonomies), but all the code below is for the use case I mentioned above.

Get all the Taxonomy Terms

First we need to get all the unique custom terms for the taxonomy, in this case “portfolio_category”:

/* Retrieves all the terms from the taxonomy portfolio_category
 *  http://codex.wordpress.org/Function_Reference/get_categories
 */
 
$args = array(
	'type' => 'portfolio',
	'orderby' => 'name',
	'order' => 'ASC',
	'taxonomy' => 'portfolio_category');

$categories = get_categories( $args );

Get the Most Recent Post for Each Term

Now we need to loop over each of the terms that was returned, and get the most recent post in that term:

/* Pulls the first post from each of the individual portfolio categories */

foreach( $categories as $category ) {

	$args = array(
		'posts_per_page' => 1,
		'post_type' => 'portfolio',
		'portfolio_category' => $category->slug,
		'no_found_rows' => true,
		'update_post_meta_cache' => false,
		'update_post_term_cache' => false
	);
	$the_query = new WP_Query( $args );
	
	// The Loop
	while ( $the_query->have_posts() ) : $the_query->the_post();
             echo '<h3>' . $category->name . '</h3>'; // Display category name 
	     the_post_thumbnail(); // Display the image of the first post in category
	endwhile;
}

// Reset Post Data
wp_reset_postdata();

Caching Results

Running WP_Query for each term in the taxonomy involves a ton of database calls, so it’s a smart idea to cache the results of these looped calls to reduce load on your server. The only time you really need to update that cached value is when a portfolio post is updated with a new category or image.

So, instead of running the above loops and outputting the values directly, we’ll save only the data we need to into an array (which I call $portoliopress_category_query) and save that as a transient. Then the template will use this cached value to output the contents.

Also, to keep the code a bit cleaner, I put this cache function in a separate file (portfolio-category-functions.php) than my regular page template code. Here’s what that function file looks like:

<?php
/**
 * @package WordPress
 * @subpackage Portfolio Press
 *
 * Loops over each of the terms in the custom taxonomy "portfolio_categories"
 * and retrieves the first post from each.  Since this is an expensive
 * request the result is built into an array and saved as a transient.
 */

function portfoliopress_category_cache() {

	/* Retrieves all the terms from the taxonomy portfolio_category
	 *  http://codex.wordpress.org/Function_Reference/get_categories
	 */
	 
	$args = array(
		'type' => 'portfolio',
		'orderby' => 'name',
		'order' => 'ASC',
		'taxonomy' => 'portfolio_category');

	$categories = get_categories( $args );
	
	$portoliopress_category_query = array();
	
	/* Pulls the first post from each of the individual portfolio categories */

	foreach( $categories as $category ) {
	
		$args = array(
			'posts_per_page' => 1,
			'post_type' => 'portfolio',
			'portfolio_category' => $category->slug,
			'no_found_rows' => true,
			'update_post_meta_cache' => false,
			'update_post_term_cache' => false
		);
		$the_query = new WP_Query( $args );
		
		// The Loop
		while ( $the_query->have_posts() ) : $the_query->the_post();
		
			$portfolio_thumbnail = null;
			$portfolio_thumbnail_fullwidth = null;
			$portfolio_thumbnail = wp_get_attachment_image_src( get_post_thumbnail_id(), 'portfolio-thumbnail');
			$portfolio_thumbnail_fullwidth = wp_get_attachment_image_src( get_post_thumbnail_id(), 'portfolio-thumbnail-fullwidth');
		
			/* All the data pulled is saved into an array which we'll save later */

			$portoliopress_category_query[$category->slug] = array(
				'name' => $category->name,
				'term_link' =>  esc_attr( get_term_link( $category->slug, 'portfolio_category' ) ),
				'portfolio-thumbnail' => $portfolio_thumbnail[0],
				'portfolio-thumbnail-fullwidth' => $portfolio_thumbnail_fullwidth[0]
			);
		
		endwhile;
   }
   
   	// Reset Post Data
	wp_reset_postdata();
	
	set_transient( 'portoliopress_category_query', $portoliopress_category_query );
	
	return $portoliopress_category_query;
}

Displaying Categories and Thumbnails in the Template

In the page template, I include the portfolio-category-functions.php file at the top. This code will only be used on this particular template, so it doesn’t make sense to load it from functions.php where it would always be loaded.

Then we check to see if the transitent has been set. If so, it’s used. If not, the portfoliopress_category_cache function is run (which will set the transitent), and its results are returned to be used:

<?php
/*
 * Template Name: Portfolio Categories
 * Description: Displays all the portfolio categories
 *
 * @package WordPress
 * @subpackage Portfolio Press
 */
 
// This template requires some additional functions to work properly
require_once( get_template_directory() . '/extensions/portfolio-category-functions.php' );

get_header();
	   
	   $portoliopress_category_query = get_transient('portoliopress_category_query');
	   if ( !$portoliopress_category_query ) {
	   		$portoliopress_category_query = portfoliopress_category_cache();
	   }
	    
	   $thumbnail = 'portfolio-thumbnail'; ?>
	   		
	   <div id="portfolio">
	   	
	   <?php
	   foreach ( $portoliopress_category_query as $portfolio_cat ) { ?>
		   
			<?php if ( $portfolio_cat[$thumbnail] ) { ?>
			<h3><a href="<?php echo $portfolio_cat['term_link']; ?>" class="title-overlay"><?php echo $portfolio_cat['name']; ?></a></h3>
			<a href="<?php echo $portfolio_cat['term_link']; ?>" class="thumb"><img src="<?php echo $portfolio_cat[$thumbnail]; ?>"></a>
			<?php } ?>
	   <?php } ?>

		</div><!-- #portfolio -->

<?php get_footer();

Updating the Transitent

Everything should now be displaying out fine, but there’s one last piece. Unless we delete the transitent when a portfolio post is updated, it will always display out the same results. So, we need to add a bit of code to functions.php that will hook in when a portfolio post is updated, and delete our saved transitent. Here’s how it works:

/**
 * Deletes the portoliopress_category_query transient if a portfolio post is updated
 */
 
function portfoliopress_save_portfolio( $post_id, $post ) {

	// If this is an auto save routine don't do anyting
	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
		return;
		
	if ( $post->post_type == 'portfolio' ) {
		delete_transient( 'portoliopress_category_query' );
	}
	
}
add_action( 'save_post', 'portfoliopress_save_portfolio', 10, 2 );

Conclusion

If someone has a lot of categories, it might also be useful to add custom pagination. I haven’t explored that functionality yet.

Does anyone use a template like this? Ideas, comments, suggestions?

This functionality is also now in Portfolio+ (see template). If anyone wants to view the actual source code you can purchase the theme and adapt it however you like.

About Devin

I am a developer based in Austin, Texas. I run a little theme shop called DevPress and help manage a WooCommerce shop with Universal Yums. Find me on twitter @devinsays.

24 Responses

  1. Adam

    Question – and maybe I am doing it wrong BUT –

    trashing, untrashing a post throws an error saying that post_type is NOT defined. and doing an isset() will not delete the transient if you just trash the post or untrash it.

    What would you suggest in this case?

  2. Tibor

    Hi, I am using Portfolio+, and appreciate the addition of the portfolio category template since I am upgrading from Portfolio Press. There is one thing that I haven’t been able to figure out:

    In the options panel I can set portfolio categories to display either full-width, or with sidebar. This is great, except that I would my portfolio category pages to display full width, and my portfolio tag pages to display with sidebar.

    How do I do this?

  3. Ro

    Devin,

    Thanks so much for making a great theme (that also comes in a free version!)

    I’m using Portfolio Press and I was wondering if there was a way to display the portfolio category at the top of the page?

    Like if someone wanted to view my illustrations. Was there a way to put “Illustration” at the top of the thumbnail page? Is this what is actually being covered in this tutorial?

    If so, should I go ahead and invest in Portfolio+?

    Thanks for your help.

  4. Byron Nilsson

    I’m just starting to set up a WP multisite and going to use Portfolio Press for each member artist. I’m concerned that either Portfolio Press or the portfolio-post-plugin are not multisite compatible.

    At the top of the Portfolio posts page I see the message about “portfolio-post-type” being required in future versions. If I Network Activate the plugin it’s not available in the individual sites in their plugins list. If I leave it deactivated in the Network admin, then it shows up in individual sites, but even though I activate it, I continue to get the message at the top saying that it is required and install it etc. … Even scarier is that I tried to Network Activate it after adding some Portfolio posts to a site, and when I went back to the site to add more the posts were scrambled and displayed an error message that portfolio-post-type was missing. I went back and Network deactivated the plugin so that it showed up in the site’s plugin list again, but the posts were still scrambled and displayed an error message. Am I doing something wrong, or should I not use either of these in a multisite install?

  5. Mark

    I’ve found a simpler solution which uses the portfolio categories to classify the portfolios. Then on each portfolio page I have a gallery.
    I nest the portfolio categories. I.e. Works is at top, under that is several types like painting, mural, photography, ilustration…
    Using the portfolio categories in the menu section on WP allows me to present the user with a nested series of choices. If they choose Work they see everything.
    If the choose a submenu item such as Illustration then they only see portfolio pages marked as portfolio category illustration.
    As I said a gallery on the page allows me to show several vies of the piece.
    The feature image for illustration is the one I choose.

  6. Is there a way to add the description of the taxonomy as well?

    I tried adding “‘description’ => $category->description,” to the “$portoliopress_category_query[$category->slug]” array, but it doesn’t seem to be adding the description field when placing “” in the template file.

    Thanks for this. It’s saving my life right now!

  7. kaas

    Hi,

    I use your code and it works great. However, I am trying to sort the posts so that the category/taxonomy with the newest post is shown first. Is that possible? It now just sorts by taxonomy name.

      1. kaas

        Was looking in the wrong place but I think I got it now!
        In the cache function, when running the query, I save the date in the ‘$portoliopress_category_query’ array. Before displaying results I sort the array by date.

  8. Steven

    Thanks for this, I thought this might be a good idea for ‘expensive’ queries.
    I have a magazine site with four multiple loops on the homepage and I would like to do something similar. Run the loops, save the array and only update when a new post is added.
    Question: does a plugin like W3 cache make this sort of thing unnecessary?

  9. Matt

    Hello – I would like to display only one category on the homepage, and thought perhaps the Portfolio+ might accomplish this with the custom portfolio template, but I can’t seem to get it to work or understand what that template does. Please advise!

Leave a Reply