Displaying WordPress Posts by Category, Even If They’re Not Recent

A couple years ago I wrote a post describing how to limit posts on your WordPress home page to ones in specific categories. A limitation of this approach is that it can only show recent posts that are in The Loop. Others have solved this problem as well, but all the solutions I’ve seen have this same limitation. What if posts in the categories you specify aren’t among your recent posts? Your home page would show no posts!

This was a problem for my new home page design. I’ve divided my site’s content into 3 major topics, and I show hyperlinked titles for the 3 most recent posts in each topic. But it’s possible that a topic may not have any posts among the most recent 10 posts, which is all The Loop knows about (10 is WordPress’ default setting for how many posts to show on your home page). So I want to get the most recent 3 posts for each topic, regardless of whether they happen to be in The Loop.

To do this, I created the following function and put it in my theme’s functions.php file. Note that I’m using a straight SQL query, which means this is not guaranteed to work in future versions of WordPress (the WP coders do a good job of maintaining a consistent programming API across versions, but they do change the database sometimes).

function get_top_category_posts ($term_ids) {
    global $wpdb;
    $top_3 = '';

    $results = $wpdb->get_results("select ID, post_title, post_date from wp_posts p
            inner join wp_term_relationships r on p.ID = r.object_id
            inner join wp_term_taxonomy t on t.term_taxonomy_id = r.term_taxonomy_id
            where t.term_id in ($term_ids) and p.post_type = 'post'
            order by post_date desc limit 3", ARRAY_A);

    foreach ($results as $result) {
        $top_3 .= "<li>" . date("M d", strtotime($result['post_date'])) . " - "
            . '<a href="' . get_permalink($result['ID']) . '">'
            . $result['post_title'] . "</a></li>\n";
    }

    return $top_3;
}

I then call it like this from my custom home page (where I’m not using The Loop at all):

<ul>
<?php echo get_top_category_posts('6,12,89,90,98,105,106,115'); ?>
</ul>

I’m passing the IDs for the categories I want. The easiest way to get a category ID is to go to its edit screen and note the cat_ID in the URL. This works for tag IDs also.

There are 3 database tables involved in the query: wp_posts contains your posts, wp_term_taxonomy contains the term IDs for categories (and tags), and wp_term_relationships connects the posts to their categories. Note that a cat_ID or tag_ID you see in an edit screen URL is actually called a term_id within the database. In this case I’m only retrieving the date and title of each post, but you could retrieve the entire post if you want (see the description of the wp_posts table).

If you want to simplify the query to improve performance, you can eliminate one of the table joins if you manually look up the term_taxonomy_id that corresponds to each category’s term_id, and pass those instead:

select ID, post_title, post_date from wp_posts
        inner join wp_term_relationships on ID = object_id
        where term_taxonomy_id in ($taxonomy_ids) and post_type = 'post'
        order by post_date desc limit 3

The downside of doing this is that every time you add a new category or tag, you’ll have to go into your database and look up its term_taxonomy_id. So I don’t recommend doing it this way unless you’re comfortable poking around in mySQL.

(Note to fellow cranky programmers: I know this is poorly abstracted – the need here is simple enough that, to me, abstraction didn’t seem worth the trouble).

Leave a Reply