WordPress Transients API – Practical examples

by Jean. 13 Comments -

The WordPress Transients API is a very useful tool which allow developers to cache data such as the result of a query for future uses. In this article, I’ve compiled a list of useful practical example to div into the power of WordPress Transients API.

What is the transients API, and why it’s useful

Most developers who worked with WordPress in the past probably ever heard of the Options API, which allow you to save, update and delete custom values. The Transients API is pretty similar to the Options API, but with the feature of an expiration time, which simplifies the process of using the wp_options database table to store cached information.

After you read the practical example I’ve listed on this post, I suggest you to read the Transients API page on WordPress Codex.

List sites from your network

Let’s start with an interesting snippet for those who run networks of many blogs. The code below display a general menu of all sites from your networks. In this case, transients are used to store the data for a defined time (which can be set using the $expires variable on line 1) so you’ll not make huge database calls each time your menu have to be displayed.

To use this snippet, first you have to paste the function into your functions.php file.

function wp_list_sites( $expires = 7200 ) {
   if( !is_multisite() ) return false;

   // Because the get_blog_list() function is currently flagged as deprecated
   // due to the potential for high consumption of resources, we'll use
   // $wpdb to roll out our own SQL query instead. Because the query can be
   // memory-intensive, we'll store the results using the Transients API
   if ( false === ( $site_list = get_transient( 'multisite_site_list' ) ) ) {
      global $wpdb;
      $site_list = $wpdb->get_results( $wpdb->prepare('SELECT * FROM wp_blogs ORDER BY blog_id') );
      // Set the Transient cache to expire every two hours
      set_site_transient( 'multisite_site_list', $site_list, $expires );
   }

   $current_site_url = get_site_url( get_current_blog_id() );

   $html = '
<ul id="network-menu">' . "\n";

   foreach ( $site_list as $site ) {
      switch_to_blog( $site->blog_id );
      $class = ( home_url() == $current_site_url ) ? ' class="current-site-item"' : '';
      $html .= "\t" . '
<li id="site-' . $site->blog_id . '" '="" .="" $class=""><a href="' . home_url() . '">' . get_bloginfo('name') . '</a></li>

' . "\n";
      restore_current_blog();
   }

   $html .= '</ul>

<!--// end #network-menu -->' . "\n\n";

   return $html;
}

Once done, the following code will display all sites from your network. Simply paste it on any of theme files, where you want the list to be displayed.

<?php
// Multisite Network Menu
$network_menu = wp_list_sites();
if( $network_menu ):
?>
<div id="network-menu">
   <?php echo $network_menu; ?>
</div>

<!--// end #network-menu -->
<?php endif; ?>

→ Source: http://wp.smashingmagazine.com/2011/11/17/wordpress…/

Twitter followers count using WordPress transients

Many blogs, including this one, are displaying how many people are following them on Twitter. It’s quite easy to grab some json data, but it takes a significant amount of time. Using transients allow you to grab the json data from Twitter once a day, and store it in your database for future uses.

Simply paste the function below into your functions.php file:

function my_followers_count($screen_name = 'kovshenin'){
	$key = 'my_followers_count_' . $screen_name;

	// Let's see if we have a cached version
	$followers_count = get_transient($key);
	if ($followers_count !== false)
		return $followers_count;
	else
	{
		// If there's no cached version we ask Twitter
		$response = wp_remote_get("http://api.twitter.com/1/users/show.json?screen_name={$screen_name}");
		if (is_wp_error($response))
		{
			// In case Twitter is down we return the last successful count
			return get_option($key);
		}
		else
		{
			// If everything's okay, parse the body and json_decode it
			$json = json_decode(wp_remote_retrieve_body($response));
			$count = $json->followers_count;

			// Store the result in a transient, expires after 1 day
			// Also store it as the last successful using update_option
			set_transient($key, $count, 60*60*24);
			update_option($key, $count);
			return $count;
		}
	}
}

echo "I have " . my_followers_count('kovshenin') . " followers";

→ Source: http://kovshenin.com/2010/05/twitter-followers-count-snippet-for-wordpress-2253/

RSS subscribers count using WordPress transients

Using exactly the same technique as demonstrated above, we can grab RSS subscribers and store the result in WordPress database. Don’t forget to update the code with your own feedburner url on line 2. Then, paste the code where you’d like to display how many RSS feed readers you have.

function feed_subscribers(){
        $feed_url = 'http://feeds.feedburner.com/yourname';
        $count = get_transient('feed_count');
        if ($count != false) return $count;
	$count = 0;
        $data  = wp_remote_get('http://feedburner.google.com/api/awareness/1.0/GetFeedData?uri='.$feed_url.'');
   if (is_wp_error($data)) {
        return 'error';
   }else{
	$body = wp_remote_retrieve_body($data);
	$xml = new SimpleXMLElement($body);
	$status = $xml->attributes();
	if ($status == 'ok') {
		$count = $xml->feed->entry->attributes()->circulation;
	} else {
		$count = 300; // fallback number
	}
   }
	set_transient('feed_count', $count, 60*60*24); // 24 hour cache
	echo $count;
}

→ Source: https://wpsnipp.com/index.php/functions-php/get-feedburner-count-using-get_transient-and-wp_remote_get/

Cached navigation menu

Introduced in WordPress 3.0, the new menu system is definitely an improvement to WordPress. But using transients, we can even do something better, a menu with the same functionality but without the huge database requests.

<?php
/**
 * Wrapper function around wp_nav_menu() that will cache the wp_nav_menu for all tag/category
 * pages used in the nav menus
 * @see http://lookup.hitchhackerguide.com/wp_nav_menu for $args
 * @author tott
 */ 
function hh_cached_nav_menu( $args = array(), $prime_cache = false ) {
	global $wp_query;
	
	$queried_object_id = empty( $wp_query->queried_object_id ) ? 0 : (int) $wp_query->queried_object_id;
	
	// If design of navigation menus differs per queried object use the key below
	// $nav_menu_key = md5( serialize( $args ) . '-' . $queried_object_id );
	
	// Otherwise
	$nav_menu_key = md5( serialize( $args ) );
	
	$my_args = wp_parse_args( $args );
	$my_args = apply_filters( 'wp_nav_menu_args', $my_args );
	$my_args = (object) $my_args;
	
	if ( ( isset( $my_args->echo ) && true === $my_args->echo ) || !isset( $my_args->echo ) ) {
		$echo = true;
	} else {
		$echo = false;
	}
	
	$skip_cache = false;
	$use_cache = ( true === $prime_cache ) ? false : true;
	
	// If design of navigation menus differs per queried object comment out this section
	//*
	if ( is_singular() ) {
		$skip_cache = true;
	} else if ( !in_array( $queried_object_id, hh_get_nav_menu_cache_objects( $use_cache ) ) ) {
		$skip_cache = true;
	}
	//*/
	
	if ( true === $skip_cache || true === $prime_cache || false === ( $nav_menu = get_transient( $nav_menu_key ) ) ) {
		if ( false === $echo ) {
			$nav_menu = wp_nav_menu( $args );
		} else {
			ob_start();
			wp_nav_menu( $args );
			$nav_menu = ob_get_clean();
		}
		if ( false === $skip_cache )
			set_transient( $nav_menu_key, $nav_menu );
	} 
	if ( true === $echo )
		echo $nav_menu;
	else
		return $nav_menu;
}




/**
 * Invalidate navigation menu when an update occurs
 */
function hh_update_nav_menu_objects( $menu_id = null, $menu_data = null ) {
	hh_cached_nav_menu( array( 'echo' => false ), $prime_cache = true );
}
add_action( 'wp_update_nav_menu', 'hh_update_nav_menu_objects' );




/** 
 * Helper function that returns the object_ids we'd like to cache
 */
function hh_get_nav_menu_cache_objects( $use_cache = true ) {
	$object_ids = get_transient( 'hh_nav_menu_cache_object_ids' );
	if ( true === $use_cache && !empty( $object_ids ) ) {
		return $object_ids;
	}




	$object_ids = $objects = array();
	
	$menus = wp_get_nav_menus();
	foreach ( $menus as $menu_maybe ) {
		if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id ) ) {
			foreach( $menu_items as $menu_item ) {
				if ( preg_match( "#.*/category/([^/]+)/?$#", $menu_item->url, $match ) )
					$objects['category'][] = $match[1];
				if ( preg_match( "#.*/tag/([^/]+)/?$#", $menu_item->url, $match ) )
					$objects['post_tag'][] = $match[1];
			}
		}
	}
	if ( !empty( $objects ) ) {
		foreach( $objects as $taxonomy => $term_names ) {
			foreach( $term_names as $term_name ) {
				$term = get_term_by( 'slug', $term_name, $taxonomy );
				if ( $term )
					$object_ids[] = $term->term_id;
			}
		}
	}
	
	$object_ids[] = 0; // that's for the homepage
	
	set_transient( 'hh_nav_menu_cache_object_ids', $object_ids );
	return $object_ids;
}

→ Source: http://hitchhackerguide.com/2011/10/07/caching-wordpress-navigation-menus-wp_nav_menu-wrapper/

Cached Tag cloud

Thanks to WordPress transients API, caching almost anything is definitely. The following example shows how to cache the good old tag cloud. Simply paste this code wherever you want you tag cloud to be displayed.

$tag_cloud = get_transient( 'tag_cloud' );
if ( false === $tag_cloud || '' === $tag_cloud ){
	$args = array('echo' => false);
	$tag_cloud = wp_tag_cloud( $args );
	set_transient( 'tag_cloud', $tag_cloud, 60*60*12 );
}
echo $tag_cloud;

→ Source: http://wpengineer.com/2148/simple-cache-with-the-wordpress-transient-api/

Caching any custom query using transients

Is your theme using custom queries? If yes, you should definitely use the transients API to cache the queries. The following code shows how to cache a custom query. As you can see, there’s nothing complicated at all.

<?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
     $special_query_results = new WP_Query( 'cat=5&order=random&tag=tech&post_meta_key=thumbnail' );
     set_transient( 'special_query_results', $special_query_results );
}

// Use the data like you would have normally...
?>

→ Source: http://codex.wordpress.org/Transients_API

  • http://webdevelopergeeks.com webdevelopergeeks

    I recently built one plugin using transient api. It is really awesome. i have searched a lot for this but the functions that you have listed are most useful as per my opinion.

  • http://ChiefAlchemist.com Mark Simchock

    It’s nice to see some more real examples. Thanks JBJ.

    For those who are interested in more examples and a bit more background, I’d like to recommend Andy Killen’s article on SpeckBoy from a couple weeks back.

    http://speckyboy.com/2011/12/14/website-speed-part-3-caching-wordpress/

    While not about transients, Parts 1 and 2 of Andy’s series are worth a go as well. Good stuff!

    Moi? The idea of transients is very intriguing. It seems to me that much of what WP does in terms of calls to the DB is often reinventing the same wheel over and over. That is, nothing has changed but the DB/server is taxed with the request. In fact, per the menu example above, it would seem to me that the WP core should make more liberal use of transients. It just makes sense, yes?

    I’d also like to suggest that with a simple function, using options can be supplemented with transients. In other words, anytime you’re using the Options API transients can be placed “in front” of that request. If the transient request comes back false/empty then fall back to the option and from there update the transients. My gawd, if you have 6 plugins and each is using options in various ways, cutting out a number of those requests would be a significant savings. (Note: I have this idea in my head but haven’t had the time to hack the code. Seems like a function of a dozen or so lines, eh?)

    But again, it would seem such functionality could be part of the WP core. That is, options and transients could be combined into a either a single API or a third API. I’m not quite sure which would be best. Or perhaps my idea is crazy? It wouldn’t be the first time I’ve been accused of such things :)

  • http://defaulttricks.com Mohit Bumb

    Please Write any tutorial about Setting API

  • http://www.coupongaadi.com rakn

    Good working examples ! Do u have any more working examples out there? I would want to use this for posts getting expired automatically (hidden) after a given amount of time.I know there are plugins…but I am not looking for them.

  • http://cashpowerinfo.com xxgCordero

    Great! Hope you could write about Setting API!

  • http://soderlind.no PerS

    Here’s anonther one, which i use in my delicious tagroll plugin:

    function ps_delicious_tagroll_get_tags($username = 'soderlind',$count = 10){

    $key = 'delicious_tagroll_' . $username;

    $feed_url = sprintf("http://feeds.delicious.com/v2/rss/tags/%s?count=%s",$username,$count);

    $rs = get_transient($key); // read "cache"
    if ($rs != false) return $rs;

    $data = wp_remote_get($feed_url.'');
    if (is_wp_error($data)) {
    return (array)get_option($key); // retrieve fallback
    } else {
    $body = wp_remote_retrieve_body($data);

    $xml = new SimpleXMLElement($body);

    if ($xml->channel->item->count() > 0) {
    $rs = json_decode(json_encode($xml->channel),TRUE); // convert xml object to array
    set_transient($key, $rs, 60*60); //save "cache", expire after 1 hour
    update_option($key, $rs); //save fallback
    } else {
    $rs = (array)get_option($key); // retrieve fallback
    }
    return $rs;
    }
    }

  • http://twitter.com/MichealKennedy Mike Kennedy

    How would you use Transient to cache total blog posts count? It’s slightly complicated to me ;)

    • http://twitter.com/MichealKennedy Mike Kennedy

      I think I got it, it seems to be caching it, but correct me if I’mwrong.

      In functions.php

      //total site post count
      function post_count() {
      global $wpdb;
      if ( false === ( $cached_post_count = get_transient( 'cached_post_count' ) ) ) {
      // It wasn't there, so regenerate the data and save the transient
      $cached_post_count = $wpdb->get_var('SELECT COUNT(*) FROM ' . $wpdb->posts . ' WHERE post_status = "publish" AND post_type = "post"');
      set_transient( 'cached_post_count', $cached_post_count, 60*60*24 );
      }
      return $cached_post_count;
      }

      Then display it anywhere in your theme:

      • http://twitter.com/MichealKennedy Mike Kennedy

        http://pastebin.com/EKz9bfV8

  • http://timelinecoverbook.com/ John

    Awesome examples dude. Thanks a bunch for posting this.

  • http://php.quicoto.com Rick

    At first sight looks awesome, I’ll try to implement this on my blog at try to test the load time.

    Thanks !

  • http://php.quicoto.com Rick

    Hi again,

    The Feedburner API is no longer available, am I wrong?

    https://developers.google.com/feedburner/

    The snippet to get the feedburner count no longer works :(

  • Trevor

    Great tip for caching wp_nav_menu, especially on a shared or less powerful server. I find wp_nav_menu super slow once you get a large menu (30+ pages) with several layers of depth.

    Only thing is there is one BIG problem with caching the wp_nav_menu. This also means the current-menu-item class gets cached too. As such your current class will never be correct. I don’t see there being a work around to this though as using the transient API for wp_nav_menu means one version of the menu is cached for all pages. Anyone have an idea to get around this, other than using page caching of course?