Speed Up Your WordPress by Caching Custom Queries using Transients API
O boy, the title sounds scary doesn’t it. You have nothing to worry because we will break it all down. Is your theme running custom WordPress queries to show random posts, popular posts, recent posts etc in the sidebar or anywhere else? If yes, then you should consider using the WordPress transient API to cache these queries to reduce resource consumption as well as helping the load time. In this article, we will show you how to speed up your WordPress site by caching custom queries using the Transients API.
Note: You need to understand how WordPress themes work (loops etc), in order for you to follow this post.
So this whole caching and transient lingo is going over my head. Well don’t worry let us explain what it does. Basically if you are running a site like List25 and have a loop that shows 6 random posts in your sidebar, then transient API can help. Every time a user refreshes the page, that custom WP Query that you have will go in your database and pull 6 posts at random. If you are a relatively new site, it shouldn’t be that bad. But if you are getting A LOT of people to your site, then it can crash your SQL server, and you will see the “Error Establishing Database Connection” screen. By adding a few extra lines of code, you can easily store the results of that query (cache it) for a certain period of time using the Transients API.
Example of the loop code that we had for pulling random posts:
<?php $random_query = new WP_Query('orderby=rand&posts_per_page=6'); while ($random_query->have_posts()) : $random_query->the_post(); ?> <div class="gridcontainer"> <div class="gridthumb"><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_post_thumbnail(); ?></a></div> <div class="gridcontent"> <div class="gridtext"><a href="<?php the_permalink() ?>" rel="bookmark"><?php the_title(); ?></a></div> </div> </div> <?php endwhile; ?>
The coolest part about our random posts query in the sidebar was it showed new content every time. So by caching the query for 12 hours, we will have the same 6 posts showing for 12 hours right? Well, we found a work around thanks to the suggestion of our friend Konstantin Kovshenin (@kovshenin). He suggested that instead of using WP_Query, we use get_posts and pull 20 posts instead. Cache the results of that query using the transients API, and then use the array_rand() function to show only 6 posts out of the original 20 at random. This way we can keep simulate the random effect on the site.
First thing we did was set the transient. We got the code from the WordPress Codex page.
// Get any existing copy of our transient data if ( false === ( $special_query_results = get_transient( 'special_query_results' ) ) ) { // It wasn't there, so regenerate the data and save the transient $randargs = array('orderby' => 'rand', 'numberposts' => 20); $special_query_results = get_posts($randargs); set_transient( 'special_query_results', $special_query_results, 60*60*12 ); }
Notice the 60*60*12 is the area where you can control the length of the cache. Feel free to change it to whatever you like. Now if we show the $special_query_results using the foreach loop, we will have all 20 posts displayed. So we need to utilize the array_rand() function to only pull 6 items at random. We added the code like this:
$randomposts = get_transient( 'special_query_results' ); $randkey = array_rand( $randomposts, 6 );
Now this will pull out 6 post IDs at random from our transient data. However, it will not pull the values for each post. So we had to do add this bits of code:
$sixposts[0] = $randomposts[$randkey[0]]; $sixposts[1] = $randomposts[$randkey[1]]; $sixposts[2] = $randomposts[$randkey[2]]; $sixposts[3] = $randomposts[$randkey[3]]; $sixposts[4] = $randomposts[$randkey[4]]; $sixposts[5] = $randomposts[$randkey[5]];
Basically we created an array for $sixposts in which we assign a value to each of those items. Not sure if this was the best way of going about it, but it worked. If any of you have better suggestions, then feel free to post it in the comments.
After doing that, we are now ready to display the loop. Simply put the code like this:
global $post; //required for it to work foreach( $sixposts as $post ) : setup_postdata($post); //All the items go here. endforeach;
setup_postdata allows you to use all loop tags inside this foreach loop such as the_permalink etc.
To make it easy for everyone, here is the final code that we have:
<?php // Get any existing copy of our transient data if ( false === ( $special_query_results = get_transient( 'special_query_results' ) ) ) { // It wasn't there, so regenerate the data and save the transient $randargs = array('orderby' => 'rand', 'numberposts' => 20); $special_query_results = get_posts($randargs); set_transient( 'special_query_results', $special_query_results, 60*60*12 ); } // Use the data like you would have normally... $randomposts = get_transient( 'special_query_results' ); $randkey = array_rand( $randomposts, 6 ); $sixposts[0] = $randomposts[$randkey[0]]; $sixposts[1] = $randomposts[$randkey[1]]; $sixposts[2] = $randomposts[$randkey[2]]; $sixposts[3] = $randomposts[$randkey[3]]; $sixposts[4] = $randomposts[$randkey[4]]; $sixposts[5] = $randomposts[$randkey[5]]; global $post; foreach( $sixposts as $post ) : setup_postdata($post); ?> <div class="gridcontainer"> <div class="gridthumb"><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_post_thumbnail(); ?></a></div> <div class="gridcontent"> <div class="gridtext"><a href="<?php the_permalink() ?>" rel="bookmark"><?php the_title(); ?></a></div> </div> </div> <?php endforeach; ?>
Ta da, now you are only making this DB query once every 12 hours no matter how many users are visiting your site.