Separating Posts by Post Format in WordPress

The Problem

WordPress has supported post formats for a couple of versions now, but I rarely ever see them being used. For this reason, I have decided to start using them myself—beginning with the “status” format. According to the Codex page, “status” posts represent short messages similar to those you would post on Twitter with the same 140-character limit. When I enabled them in my theme, I noticed that they were mixed in with the regular blog posts on the blog listing page. In my opinion, these little updates deserve their own space above the other posts.

The Solution

How did I achieve my goal? At first I added the function “has_post_format()” to show only regular posts, like so:

<?php while ( have_posts() ) : the_post() ?>
<?php 	if ( ! has_post_format('status') ) : ?>
	<!-- post loop html code goes here; omitted for brevity -->
<?php	endif; ?>
<?php endwhile; ?>

I preceded this with the following code (borrowed from a post on Stack Overflow) to only show the status updates:

<?php
$args 		= array( 'post_type'	=> 'post',
			 'post_status' 	=> 'publish',
			 'order' 	=> 'DESC',
			 'tax_query' 	=> array( array( 'taxonomy' => 'post_format',
							 'field'    => 'slug',
							 'terms'    => array( 'post-format-status' ) ) ) );
$status_updates	= get_posts( $args );
if ( $status_updates ) :
?>
<h1>Status Updates</h1>
<?php
foreach ( $status_updates as $post ) :
	setup_postdata( $post );
?>
<div id="post-<?php the_ID() ?>" class="post">
	<div class="post-meta">
		<h3><?php the_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ); ?></h3>
	</div>
	<div class="post-content">
		<?php the_excerpt(); ?>
	</div>
</div>
<?php
	endforeach;
endif; ?>

If you are intimately familiar with WordPress, this code should look fairly standard to you. The key part of this code is the “tax_query” key provided to the “$args” array and its subsequent values. Post formats are stored in their own taxonomy, with the name of the format prefixed by the string “post-format-” and stored as a term. (Hint: To show other formats, add them to the array as necessary or change the current key to which ever format you want to display.)

This worked great, but I wanted to make it more universal and easier to re-use. After some trial and error, I managed to modify a portion of the code from the first example to filter out the status updates.

function my_pre_get_posts( $query ) {
	if ( $query->is_posts_page && $query->is_main_query() ) {
		$query->set("tax_query", array( array( 'taxonomy' => 'post_format',
						       'field'	  => 'slug',
						       'terms'	  => array( 'post-format-status' ),
						       'operator' => 'NOT IN' ) ) );
	}

	return $query;
}
add_action( 'pre_get_posts', 'my_pre_get_posts' );

A WordPress hook, called “pre_get_posts“, intercepts the query used in the main loop for the post listing page. The “$query->is_main_query()“ is a new method introduced in WordPress 3.3 which ensures that we do not mess up any other queries being used, such as the one that retrieves the status updates. If you look closely, you’ll notice that I am re-using the previously mentioned “tax_query” argument but I have added the “operator” parameter to tell “WP_Query” that we want to ignore this specific term.

Caveats

There is a downside to this method, however. By removing the status updates from the main loop, the total number of posts (including the status updates) may be greater than your “max posts per page” setting thereby throwing your pagination off. In the future, I plan to find a way around this (probably by using the “paginate_links()” function and some simple math) but to me it is not that big of a deal right now.

Conclusion

As always, I hope that you have found this post useful and that it has helped you in some way. If you have a question, comment, or suggestion, do not hesitate to respond to this post using the form provided here or ask your question on the WordPress StackExchange.

<legal_disclaimer>
Please remember that the code that I have provided is for illustrative purposes only and may not be ready for use in production environments without further modification. I accept absolutely no responsibility for any damage or harm that may come to your site or anyone else’s site through your use or abuse of this code.
</legal_disclaimer>