How to Prioritize Posts from A Category on the Archive Page

By default, WordPress shows the most recent posts on your blog home page. There are plugins to designate certain posts are “Featured” so they show at the top.

There are solutions to show posts from one specific category, but very few talk about how to query and prioritize posts from a category using a single query. And that’s what I will show you today.

First, let’s look at the SQL produced by the standard WordPress WP_Query:

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID 
FROM wp_posts  WHERE 1=1  
AND ((wp_posts.post_type = 'post' 
AND (wp_posts.post_status = 'publish')))  
ORDER BY wp_posts.post_date DESC LIMIT 0, 10

This SQL query is looking for posts that are published, ordered by the most recently published.

Running the query gives me the following:

It’s just a list of IDs of my most recently published blog post.

One way to prioritize by category is by changing the SQL to order by the specific category.

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID, wtr.term_taxonomy_id as priority 
FROM wp_posts 
LEFT JOIN wp_term_relationships wtr 
ON wp_posts.ID = wtr.object_id AND wtr.term_taxonomy_id = 6 
WHERE 1=1 
AND ((wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish')))
ORDER BY priority DESC, post_date DESC LIMIT 0, 10

The highlighted text are my additions.

Basically, we have to join the wp_posts table with the wp_term_relationships table to tell which post is in the prioritized category. In this example, the category ID that I’m prioritizing is 6.

The result from this SQL is:

There are 2 posts that are in the Quick Tips category, so the “Priority” column shows 6. For the posts that aren’t in that category, the column shows NULL.

How to Change the SQL

WordPress provides 3 filters that allows you to change different sections of the SQL statement: post_fields (SELECT), posts_join, posts_orderby.

This code can go in the functions.php of your child theme.

1. Add the “priority” column to the SELECT SQL:

function jc_prioritize_select_clause($select, $wp_query) {

	if (!is_admin() && is_home()) {
		$select .= ", wtr.term_taxonomy_id as priority";
	}

	return $select;
}

add_filter( 'posts_fields', 'jc_prioritize_select_clause', 20, 2 );

2. Join the wp_posts table to wp_term_relationships

function jc_prioritize_join_clause($join, $wp_query) {

	if (!is_admin() && is_home()) {
		$join .= " LEFT JOIN wp_term_relationships wtr 
			ON wp_posts.ID = wtr.object_id AND wtr.term_taxonomy_id = 6"; 
                        // change the number to the category ID you want to prioritize
	}

	return $join;
}

add_filter( 'posts_join', 'jc_prioritize_join_clause', 20, 2 );

3. Finally, order the results by the new “priority” column and then the post_date.

function jc_prioritize_orderby_clause($orderby, $wp_query) {
	if (!is_admin() && is_home()) {
		$orderby = "priority DESC, post_date DESC"; 
	}
}

add_filter( 'posts_orderby', 'jc_prioritize_orderby_clause', 20, 2 );

You’ll notice the !is_admin() and is_home() conditions.

The !is_admin() skips running this filter on the admin backend because if we do, then the Quick Tips posts will always show up first in the Admin backend too.

The is_home() isolates the function to only run when it’s on the blog listing page (which is what I want here).

If you have other places where you want to prioritize the category, you can take out the is_home() condition.

Does this work for custom post types?

Yes it does! To specify that you only want the filter to apply to specific post types, you can simply add to the conditional statement:

function jc_prioritize_select_clause($select, $wp_query) {

	if (!is_admin() && $wp_query->get("post_type") === "my_custom_post_type") {
		$select .= ", wtr.term_taxonomy_id as priority";
	}

	return $select;
}

Scroll to Top