Using Transients to Show Random Posts on WordPress The Right Way

Heads up: This article is a tech read and assumes you have working knowledge of the WordPress loop. If not, I fear transients may bore you to tears.

Quite the loaded title, yes, but allow me to explain. Like many WordPress developers I found ease in using the ‘orderby’ => ‘rand’ parameter to display random posts, products, etc. in my queries. Little did I know the stress this greedy little guy could put on a server, especially if you have a large database of posts. Below we will explore the pitfalls of using this, followed by the benefits and methods of using the WordPress Transients API to cache posts to display at random.

Take this simple loop using WP_Query:

<?php
$args = array(
    'post_type' => 'post',
    'posts_per_page' => 5,
    'orderby' => 'rand',
);
$my_query = new WP_Query($args);

if ( $my_query->have_posts() ) :
    while ( $my_query->have_posts() ) : $my_query->the_post();
        // echo $post content
    endwhile;
    wp_reset_postdata();
endif; ?>

I used to think that it was just plucking 5 posts and then ordering them randomly, but what’s actually happening is that the query is looking at every post row in the database and then selecting 5 random posts from it. When a blog is just starting out this isn’t really an issue, but think about the stress this can place on the server of a website with thousands of posts and regular visitors. Every time someone visits that page a new query must be run. This can slow down your site and even result is some visitors getting an error when trying to access a page. I discovered this after pushing a client’s website up to their host, WPEngine, who by default disable the ‘orderby’ => ‘rand’ parameter specifically for this reason. They recommend caching results for a limited time, which lightens the request load on the server significantly. This is when I discovered transients.

Transients allow you to cache data. They are essentially options you save to the WordPress options table but these settings can have an expiration. The expiration is a maximum date that the transient would expire. If you set the cache to expire in 12 hours it may expire any time up to that, but never after it. They can be used to cache anything. Plugin developers often use them to store licence expiry dates. We’re going to use it to cache our query using two methods.

Method 1:

<?php
if ( ( $my_query = get_transient('my_query_cached') ) === false ) :
    $args = array(
        'post_type' => 'post',
        'posts_per_page' => 5,
        'orderby' => 'rand',
    );
    $my_query = new WP_Query($args);

    set_transient('my_query_cached', $my_query, 1 * HOUR_IN_SECONDS);
endif;

if ( $my_query->have_posts() ) :
    while ( $my_query->have_posts() ) : $my_query->the_post();
        // echo $post content
    endwhile;
    wp_reset_postdata();
endif; ?>

How easy is that? Yes I know it’s still using ’orderby' => 'rand' but this version only runs one query for it every hour, no matter how many visitors are using the site.

What we’re doing is checking to see if a transient called ‘my_query_cached’ exists by setting it and checking for what it returns. If it returns false, we set up the query as per usual and cache those posts to a new transient for up to 1 hour. WP_Query is serialized and stored as a transient. While retrieving it gets deserialized. If it is set, the data stored in the transient is set to $my_query in the conditional statement and used in the loop. Note: It will display those same 5 posts every time the page is loaded until the transient expires, at which point it will run a new query, get new random posts and set them to the new transient. If you want to display new posts every time the page is loaded, a different approach is needed. Enter Method 2.

Method 2:

<?php
if ( ( $my_query = get_transient('my_query_cached') ) === false ) :
    $args = array(
        'post_type' => 'post',
        'posts_per_page' => 50,
    );
    $my_query = get_posts($args);

    set_transient('my_query_cached', $my_query, 1 * HOUR_IN_SECONDS);
endif;

// Change the number argument here and in the for loop below
// to set how many posts are displayed
$rand_key = array_rand($my_query, 5);

// Pull values manually for each post
for ($i = 0; $i < 5; $i++) {
    $rand_posts[$i] = $my_query[$rand_key[$i]];
}

foreach ($rand_posts as $post) :
    setup_postdata($post);
        // echo $post content
    wp_reset_postdata();
endforeach; ?>

Here we’re omitting ’orderby' => 'rand' from the query arguments. Instead we store the last 50 posts in a transient, and then use array_rand() to pick 5 random post IDs out of those 50 every time the page is loaded. It may not be a true random query, but to the visitor it will appear so each time they visit. With our 5 random post IDs, we need to pull the value for each post so we run the for loop to do this, adding the post values to a new array. Then we run a foreach loop on the new $rand_posts array, setting up postdata for each $post. You can then use WordPress post functions as per usual.

Tip: If you’d like to see your website’s transients, the Transients Manager plugin adds a UI to WordPress where you can view, search, edit, and delete transients at will. It also displays their value and expiry date.

The beauty of this is that, like many WordPress features, you don’t have to fully grasp how transients work to use them. You can play around with just the get_transient() and set_transient() functions with little understanding. No deep dive required.

Ok, just a few quick questions.

After you introduce yourself and your project, I'll get it touch with you to schedule a time to chat. You should expect to hear from me in a day or so.

Splendid.

Your message just arrived in my inbox. Talk to you soon.