Tutorial

A Guide To Transients In WordPress

Draft updated on Invalid Date
Default avatar

By Daniel Pataki

A Guide To Transients In WordPress

This tutorial is out of date and no longer maintained.

Introduction

WordPress provides us with a wonderful API for managing options. Options are ridiculously simple: they each have a name and a value and are stored in the WordPress database - in the options table - for safekeeping. Options can store anything from your Google Maps API key to an array of data or even the HTML code for your sidebar.

Transients are similar in all but one property; they have an expiration time. This makes them uniquely suited to act as a cache for appropriate data. In addition to reducing the number of queries our website utilizes, they can be further sped up by caching plugins which may make WordPress store transients in fast memory (like Memcached) instead of the database.

In this post, I’ll take you through the code you need to know to use transients and show you some advanced examples of how they can be utilized. I’d like to focus on the fact that transients can be used as an easy cache mechanism for any object, not just simple strings or WordPress-related objects.

Basic Operations

The three basic operations available are: get, set, and delete. Each operation has a site-specific version and a network-wide version which is used if a transient should be made available to the whole multisite network. Based on this extremely simple interface we can build pretty sophisticated systems to speed up our website considerably.

Setting Transients

By using the set_transient() function we can create a transient. The function takes two required and one optional parameter. The first one is the name of the transient, the second is the value, the third is the expiration time.

    set_transient( 'my_mood', 'Pretty Awesome', 28800 ); // Site Transient
    set_site_transient( 'my_mood', 'Pretty Awesome', 28800 ); // Multisite Transient

When the code above is executed it stored my current mood for 28800 seconds (8 hours). Note that you can not retrieve the value of the transient after the expiration date, it will no longer exist.

Getting Transients

Retrieving the value of transients is even simpler than setting them. By utilizing the get_transient() functions and passing the transient name as the first - and only - parameter we can retrieve the value of our transient.

    $my_mood = get_transient( 'my_mood' ); // Site Transient
    $my_mood = get_site_transient( 'my_mood' ); // Multisite Transient

Be mindful of the return value when creating your transients! If the transient has expired, doesn’t exist, or doesn’t have a value the function returns ‘false’. This means that you should use the identity operator (===) instead of the equality operator (==) when checking the value. It also means that you should not store plain boolean values, convert them to integers instead.

Deleting Transients

In many cases, you’ll want to delete transients before they expire which is when the delete_transient() function comes in handy. It takes one parameter, the name of the transient.

    delete_transient( 'my_mood' ); // Site Transient
    delete_site_transient( 'my_mood' ); // Multisite Transient

Expiration In-Depth

The most important thing to keep in mind is that transients that never expire - have an expiration set to 0 - are autoloaded, other transients are not autoloaded. We can use this to our advantage by figuring out which transients are used frequently.

Never Expiring Transients

So what is the point of a transient which never expires? You could use it to store a custom recent posts widget for example. This transient can be deleted and re-set when a new post is published so we don’t need to give it an expiration. Since the expiration is set to 0 the transient is autoloaded which is perfect if we want to show it on all pages.

If you only show the widget on your about page there really is no need to autoload the transient. In this case, it is better to give it an extremely large expiration date like 3153600000 which would be 100 years.

Time Constants

Since WordPress 3.5 time constants have been available to give developers easy access to time in seconds.

    set_transient( 'my_mood_today', 'Pretty Awesome', DAY_IN_SECONDS );

The following five constants can be used:

    set_transient( 'my_mood_today', 'Pretty Awesome', MINUTE_IN_SECONDS );
    set_transient( 'my_mood_today', 'Pretty Awesome', HOUR_IN_SECONDS );
    set_transient( 'my_mood_today', 'Pretty Awesome', DAY_IN_SECONDS );
    set_transient( 'my_mood_today', 'Pretty Awesome', WEEK_IN_SECONDS );
    set_transient( 'my_mood_today', 'Pretty Awesome', YEAR_IN_SECONDS );

The Uses Of Transients

When talking about transients the biggest error people make is that they seem to think it is for discreet data only. My mood, time of day, current temperature. All good, all time-sensitive so all ripe for transiency.

Looking beyond simple data you can figure out tons of great ways to use transients. Think about data that changes on your site, but not too frequently. A widget or a whole sidebar could be a good example. Is it really necessary to perform database queries each time your sidebar is shown?

Creating the navigation menu is also a pretty database-intensive task. Once the website admin has assembled the required menu structure it rarely changes. Why not load the whole thing from a transient? Using hooks we can still force a re-load if menu items change.

How about a section at the bottom of every single post which shows a couple of interesting posts from your archives. Why pull this from the database on every single page load when you could cache it as a transient and re-load it no more than once every hour?

Caching The Navigation Menu

A theme usually uses wp_nav_menu() to output a menu. Parameters are passed to this function and the correct menu is displayed. This function returns the menu in HTML form which is great, it’s a string we can easily store as a transient. Let’s create a function which will replace wp_nav_menu() in our theme.

    function scotch_transient_menu( $args = array() ) {
        $defaults = array(
            'menu' => '',
            'theme_location' => '',
            'echo' => true,
        );
    
        $args = wp_parse_args( $args, $defaults );
    
        $transient_name = 'scotch_menu-' . $args['menu'] . '-' . $args['theme_location'];
        $menu = get_transient( $transient_name );
    
        if ( false === $menu ) {
            $menu_args = $args;
            $menu_args['echo'] = false;
            $menu = wp_nav_menu( $menu_args );
            set_transient( $transient_name, $menu, 0 );
        }
    
        if( false === $args['echo'] ) {
            return $menu;
        }
    
        echo $menu;
    
    }

Our scotch_transient_menu() function takes the same arguments as wp_nav_menu(). I’ve made sure that the menu, theme_location and echo parameters have default values. Since we use outside of passing them to wp_nav_menu() they must have a value, otherwise, a PHP notice will be thrown.

For each menu, we create a transient with the naming scheme of scotch_menu-[menu]-[location]. This allows for flexible use within themes that may call for multiple menus. If you have a single menu you don’t need to get fancy like this, you can just hard-code this with its menu ID.

We try and retrieve the menu using the get_transient() function. Remember that this should return false if the transient has expired, has no value, or doesn’t exist. We use this to determine if we need to refresh our cache.

If the $menu variable is false, I create a new array of options to pass to the wp_nav_menus() function. The reason for this is that we need to make sure the echo parameter is false while retrieving the menu. We may echo it later, but we need the return data first to store it as the transient.

The next step is to set the transient. Since the menu is likely to be shown on every page I thought giving it an expiration of 0 would be best, since it would be autoloaded.

So far so good, our menu is now cached and served with the minimum amount of fuss. The problem we’re facing now is that changes in our menus will never be reflected on our site. This is where hooks come in which allows us to update our cached value.

The wp_update_nav_menu action is fired whenever a menu item is updated. If we create a function that updates our cached value each time we are in the clear.

    add_action( 'wp_update_nav_menu', 'scotch_update_menus' );
    function scotch_update_menus() {
        global $wpdb;
        $menus = $wpdb->get_col( 'SELECT option_name FROM $wpdb->options WHERE option_name LIKE "scotch_menu-%" ' );
        foreach( $menus as $menu ) {
            delete_transient( $menu );
        }
    }

This is a bit crude but it does the job. Since we need to find all menus possible we need to manually look through the database to match our naming convention. This would also be a lot easier if you just need to cater to a specific menu.

In conclusion, our menus are now highly optimized. They are served from an autoloaded transient so no database queries are necessary apart from loading the option. Whenever a menu item changes our transient is deleted and the menu will be rebuilt whenever someone accesses the site.

Creating A Recent Projects Section

Suppose you have a personal site and on your about page you list your recent projects. The list comes from a custom post type, the code looks something like this:

    $args = array(
        'post_type'   => 'project',
        'post_status' => 'publish'
    );
    $projects = new WP_Query( $args );
    
    if ( $projects->have_posts() ) {
        echo '<ul>';
        while( $projects->have_posts() ) {
            echo '<li><a href="' . get_permalink() . '">' . the_title( '', '', false ) . '</a></li>';
        }
        echo '</ul>';
    }

Each time the page is loaded the database query is made, even if you take on a new project every 5 years. What a waste! Let’s fix this, shall we? We’ll use the same thinking as we did in our previous example, with two small changes.

    $projects = get_transient( 'scotch_cached_projects' );
    if ( false === $projects ) {
        $args = array(
            'post_type'   => 'project',
            'post_status' => 'publish'
        );
    
        $projects = new WP_Query( $args );
    
        set_transient( 'scotch_cached_projects', $projects, DAY_IN_SECONDS );
    }
    
    if( $projects->have_posts() ) {
        echo '<ul>';
        while( $projects->have_posts() ) {
            echo '<li><a href="' . get_permalink() . '">' . the_title( '', '', false ) . '</a></li>';
        }
        echo '</ul>';
    }

In this instance, we used an actual expiration time. This would be appropriate if you really don’t care if your new project shows up a day after you add it to your site.

If making sure that the project shows up immediately is important then I suggest adding a large expiration time. While we could set it to 0, this would make this transient autoload and we don’t really need it on every page. So in this case we’ll give it a far-future expiration and create a function to flush our transient when a new project is published.

    add_action( 'publish_post', 'scotch_purge_project_transient' );
    function scotch_purge_project_transient( $ID, $post ) {
        if ( 'project' == $post->post_type ) {
            delete_transient( 'scotch_cached_projects' );
        }
    }

Conclusion

I hope it is clear from this article that transients are much more than a cute little companion to the options API. When used correctly they provide a huge speed benefit to your website or at least decrease the resources used by your server.

Transients can store anything from an array of your most active commenters to whole post objects if needed.

Always keep in mind that caching can lead to a decrease in usability. Just because something can be stored as a transient doesn’t mean it should be stored there.

If you use the transients API for something particularly awesome let us know in the comments!

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Daniel Pataki

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel