Advertisement
  1. Code
  2. Plugins

Getting Started with the WordPress Transient API, Part 2

Scroll to top
Read Time: 8 min

In the first post in this series, we took defined what the API is, how it differs form the settings API, and some of the general calls that we can make to the API. In this post, we'll be taking a look at a practical implementation of the API and how to handle some idiosyncrasies that come with dealing with expired data.

The WordPress Transients API is a powerful (but really-easy-to-use) aspect of the WordPress API. Generally speaking, it makes it really easy to store data with an expiration time, and makes it really easy to take advantage of various caching plugins to ultimately increase the speed of your site.


Setup The Plugin

For the purposes of this plugin, we're going to create a simple widget that will list a blogs top commenters of all time. The goal of the plugin is to keep it lean so that we can highlight the transients functionality of the plugin.

Note that all of the plugin's files can be retrieved from GitHub at any time. In the mean time, go ahead and create a directory called 'top-commenters-cached' and make sure that it has the following directory structure:

If you're not interested in localizing the plugin, feel free to leave the 'lang' directory out of the plugin. At this point, we're ready to begin writing the widget.


Basic Functionality

The plugin is simple. It should…

  • Allow the user to give the widget a custom
  • Retrieve the top 10 most popular commenters of the life of the blog

Easy enough. Here's the code for the basic plugin. Note that it's commented throughout so spend sometime reading through it to understand what we're doing. If you're unfamiliar with the widget API, don't forget to check out our WordPress Widget Boilerplate post.

1
2
class Top_Commenters_Cached extends WP_Widget {
3
4
	const name = 'Top Commenters (Cached!)';
5
	const locale = 'top-commenters-cached-locale';
6
	const slug = 'top-commenters-cached';
7
	
8
9
	/*--------------------------------------------------*/
10
	/* Constructor

11
	/*--------------------------------------------------*/
12
	
13
	/**

14
	 * The widget constructor. Specifies the classname and description, instantiates

15
	 * the widget, loads localization files, and includes necessary scripts and

16
	 * styles.

17
	 */
18
	function Top_Commenters_Cached() {
19
20
		$widget_opts = array (
21
			'classname' => self::name, 
22
			'description' => __('A plugin used to demonstrate the WordPress Transients API for an Envato blog series.', self::locale)
23
		);	
24
		$this->WP_Widget(self::slug, __(self::name, self::locale), $widget_opts);
25
		
26
		load_plugin_textdomain(self::locale, false, dirname(plugin_basename( __FILE__ ) ) . '/lang/' );
27
		
28
	} // end constructor

29
30
	/*--------------------------------------------------*/
31
	/* API Functions

32
	/*--------------------------------------------------*/
33
	
34
	/**

35
	 * Outputs the content of the widget.

36
	 *

37
	 * @args			The array of form elements

38
	 * @instance

39
	 */
40
	function widget($args, $instance) {
41
	
42
		extract($args, EXTR_SKIP);
43
		
44
		echo $before_widget;
45
		
46
		$widget_title = empty($instance['widget_title']) ? '' : apply_filters('widget_title', $instance['widget_title']);
47
		$commenters = $this->query_for_commenters();
48
    
49
		// Display the widget

50
		include(WP_PLUGIN_DIR . '/' . self::slug . '/views/widget.php');
51
		
52
		echo $after_widget;
53
		
54
	} // end widget

55
	
56
	/**

57
	 * Processes the widget's options to be saved.

58
	 *

59
	 * @new_instance	The previous instance of values before the update.

60
	 * @old_instance	The new instance of values to be generated via the update.

61
	 */
62
	function update($new_instance, $old_instance) {
63
		
64
		$instance = $old_instance;
65
		
66
		$instance['widget_title'] = $this->strip($new_instance, 'widget_title');
67
    
68
		return $instance;
69
		
70
	} // end widget

71
	
72
	/**

73
	 * Generates the administration form for the widget.

74
	 *

75
	 * @instance	The array of keys and values for the widget.

76
	 */
77
	function form($instance) {
78
	
79
		$instance = wp_parse_args(
80
			(array)$instance,
81
			array(
82
				'widget_title' => ''
83
			)
84
		);
85
	
86
		$widget_title = $this->strip($instance, 'widget_title');
87
		
88
		// Display the admin form

89
    	include(WP_PLUGIN_DIR . '/' . self::slug . '/views/admin.php');
90
		
91
	} // end form

92
93
	/*--------------------------------------------------*/
94
	/* Private Functions

95
	/*--------------------------------------------------*/
96
	
97
	/**

98
	 * Retrieves the weekly top commenters for the past week and stores the values in the cache.

99
	 * If the cache is empty, then the function will request information from the database and 

100
	 * store it in the cache.

101
	 */
102
	private function query_for_commenters() {
103
	
104
		$commenters = null;
105
		
106
		// query the database for the top commenters

107
		global $wpdb;
108
		$commenters = $wpdb->get_results("

109
			select count(comment_author) as comments_count, comment_author, comment_type

110
			from $wpdb->comments

111
			where comment_type != 'pingback'

112
			and comment_author != ''

113
			and comment_approved = '1'

114
			group by comment_author

115
			order by comment_author desc

116
			LIMIT 10

117
		");
118
119
		return $commenters
120
	
121
	} // end query_for_commenters

122
	
123
	/*--------------------------------------------------*/
124
	/* Helper Functions

125
	/*--------------------------------------------------*/
126
  
127
	/**

128
	 * Convenience method for stripping tags and slashes from the content

129
	 * of a form input.

130
	 *

131
	 * @obj			The instance of the argument array

132
	 * @title		The title of the element from which we're stripping tags and slashes.

133
	 */
134
	private function strip($obj, $title) {
135
		return strip_tags(stripslashes($obj[$title]));
136
	} // end strip

137
	
138
} // end class

139
add_action('widgets_init', create_function('', 'register_widget("Top_Commenters_Cached");')); 
140
?>

Next, let's take a look at the widget's view. This is the part of the plugin that's responsible for displaying the list of comments. It works by displaying the widget's title (if it's defined), then loops through the results creating a new list item.

1
2
<?php if(strlen(trim($widget_title)) > 0) { ?>
3
	<h3 class="widget-title">
4
		<?php echo $widget_title; ?>
5
	</h3>
6
<?php
7
	} // end if

8
		
9
	global $wpdb;
10
	$comment_list = '<ol>';
11
	foreach($commenters as $commenter) {
12
		
13
		$comment_list .= '<li>';
14
			
15
			// actually print the commenter's name and the number of comments

16
			$comment_list .= $commenter->comment_author;
17
			$comment_list .= ' (' . $commenter->comments_count . ')';
18
19
		$comment_list .= '</li>';
20
		
21
	} // end foreach

22
	$comment_list .= '</ol>';
23
	
24
	echo $comment_list;
25
?>

Obviously, we've left out part of the code. Namely, the admin panel. It should simply allow for users to enter a title for their widget:

1
2
<div>
3
	<fieldset>
4
		<legend>
5
			<?php _e('Widget Options', self::locale); ?>
6
		</legend>
7
		<label for="<?php echo $this->get_field_id('widget_title'); ?>" class="block">
8
			<?php _e('Title:', self::locale); ?>
9
		</label>
10
		<input type="text" name="<?php echo $this->get_field_name('widget_title'); ?>" id="<?php echo $this->get_field_id('widget_title'); ?>" value="<?php echo $instance['widget_title']; ?>" class="" />		
11
	</fieldset>
12
</div>

Remember that you can view the full source code and download the plugin from its GitHub repository.


Cache The Data

At this point, we have a functional plugin; however, we're not actually caching any data yet. The most intensive part of this plugin is when we're querying the database and the results of the query are what we actually want to cache so let's do that.

Locate the query in the code:

1
2
	global $wpdb;
3
	$commenters = $wpdb->get_results("

4
		select count(comment_author) as comments_count, comment_author, comment_type

5
		from $wpdb->comments

6
		where comment_type != 'pingback'

7
		and comment_author != ''

8
		and comment_approved = '1'

9
		group by comment_author

10
		order by comment_author desc

11
		LIMIT 10

12
	");

And let's store the results for 12 hours using the transients API:

1
2
set_transient('top_commenters_cached', $commenters, 60 * 60 * 12);

Pretty easy, right? Of course, we're not done yet.


Retrieve The Data

Once the transient is set, we need to be able to retrieve the transient. Let's set that up now:

1
2
private function query_for_commenters() {
3
	return get_transient('top_commenters_cached');
4
} // end query_for_commenters

That's all there is to it!

But wait - if you recall from the first post in the series, transients actually expire so we're not guaranteed to retrieve the transient.


Finding Missing Data

Regardless of what you're doing, retrieving data that has expired generally follows the same process:

  • Check for the existence of the transient
  • If it exists, use it
  • If it doesn't exist, set it then retrieve it

So let's do that within the context of our plugin:

1
2
private function query_for_commenters() {
3
4
	$commenters = null;
5
	
6
	// check to see if the transient exists. set it if it's expired or missing

7
	if(!get_transient('top_commenters_cached')) {
8
9
		// query the database for the top commenters

10
		global $wpdb;
11
		$commenters = $wpdb->get_results("

12
			select count(comment_author) as comments_count, comment_author, comment_type

13
			from $wpdb->comments

14
			where comment_type != 'pingback'

15
			and comment_author != ''

16
			and comment_approved = '1'

17
			group by comment_author

18
			order by comment_author desc

19
			LIMIT 10

20
		");
21
22
		// store the result 

23
		set_transient('top_commenters_cached', $commenters, 60 * 60 * 12);
24
		
25
	} // end if 

26
	
27
	// transient is guaranteed to exist now, so return it

28
	return get_transient('top_commenters_cached');
29
30
} // end query_for_commenters

Conclusion

Not too bad, right?

As you can see, working with the Transients API requires little more than knowing when to use it and what functions are available. In my opinion, it's one of the most powerful aspects of the WordPress API.

If you find yourself retrieving large amounts of data, looking for a way to expire data for a refresh, or simply wanting to take advantage of caching plugins, remember to take advantage of the Transients API.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.