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.
