Using Cookies in WordPress, Part II: Cache-Busting with Ajax

Caching makes everything harder. If you’re writing good code and then you—and your clients, coworkers, etc.—are seeing absolutely no result, the issue is typically caching. In fact, a lovingly cited quote by Phil Karlton holds that “There are only two hard things in Computer Science: cache invalidation and naming things.”

The Challenge: Full-Page Caching

So it stands to reason that last week’s article on writing PHP cookies in WordPress has a big asterisk about caching. In particular, the full-page caching that makes our site fast (it’s the SiteGround SuperCacher’s dynamic caching, which we love) also causes cookies not to be retrieved.

To be more specific, it’s possible to save new cookies to the server, but the HTTP $_COOKIES object doesn’t reflect those changes, because the server’s not looking for changes: It’s serving a cached version of the entire page, headers included. This is beautifully explained (and was made clear to me) in this article by Nick Davis.

So following a puzzled (and puzzling) comment on the article, I had to turn off dynamic caching for the page to make the demo work—which, of course, may not be workable on a site which needs full-page caching and cookies. You shouldn’t have to choose one or the other.

The Solution: Ajax

It turns out there’s a solution, which makes use of Ajax. Ajax is a way to talk directly to your web server after your page loads—and then to update your page with the results of that conversation. It’s just what we need to get around a full-page cache, because our issue is as follows: the new cookie information is set in the server, but the server doesn’t look for it when it builds out the page. So we’re going to dive into the server and get that information out ourselves, using Ajax.

A Working Demo



This works a lot like last week’s demo, but it:

  1. Doesn’t force page refreshes.
  2. Works with page caching (SiteGround’s dynamic caching) enabled on the page.

Our Sources

Our solution here is an elabortion of Nick Davis’s article, and a nice WP Engine article on cookies and WordPress caching. We’re also using a simple JS cookie library, as recommended by Paul Underwood.

This plugin is up on GitHub if you’d like to browse the full source. We’ll just be calling out bits of it below.

The Code

PHP

Relative to last week, we’re doing significantly less in PHP. We’re now doing three things:

1. Enqueue JavaScript Files

add_action( 'wp_enqueue_scripts', 'wpshout_cookie_scripts' );
function wpshout_cookie_scripts() {
	wp_enqueue_script( 'wpshout-js-cookie-demo', plugin_dir_url( __FILE__ ) . 'wpshout-js-cookie-demo.js', array( 'jquery', 'cookie' ) );
	wp_enqueue_script( 'cookie', plugin_dir_url( __FILE__ ) . 'cookie.js', array( 'jquery' ) );

	/* Telling the JS file where ajaxUrl is */
	wp_localize_script( 'wpshout-js-cookie-demo', 'ajaxUrl', array( 
		'url' => admin_url() . 'admin-ajax.php',
	) );
}

This code pulls in the two JS files we need: cookie.js, the external library for JavaScript cookies; and wpshout-js-cookie-demo.js, which relies on cookie.js.

The call to wp_localize_script() is our way of giving our JS file PHP variables. In our case, we need to do something common: Tell our script where Ajax requests should point to.

2. Handle Ajax Requests

add_action( 'wp_ajax_wpshout_get_fave_food_cookie', 'wpshout_get_fave_food_cookie' );
add_action( 'wp_ajax_nopriv_wpshout_get_fave_food_cookie', 'wpshout_get_fave_food_cookie' );
function wpshout_get_fave_food_cookie() {
	$cookie = $_POST['cookie'];
	echo $_COOKIE[ $cookie ];
	die;
}

This code does the server-side processing for our Ajax request. What it does, in words, is look up a specific member of the $_COOKIE superglobal array. We define which member that will be based on what we POST to Ajax with the name 'cookie'.

Once the $_COOKIE item is fetched, this code gives it back to the JS that called it using echo, and then stops executing.

3. Register Shortcodes

add_shortcode( 'js_cookie_demo', 'wpjcd_show_cookie_result' );
function wpjcd_show_cookie_result() {
	ob_start();
	echo '<div class="current-favorite"></div>';
	return ob_get_clean();
}
add_shortcode( 'js_cookie_form', 'wpjcd_show_cookie_form' );
function wpjcd_show_cookie_form() {
	ob_start(); ?>
		<div class="favorite-food-form">
			<label for="name">Fave Food:</label><br>
			<input type="text" class="favorite-food-input" placeholder="Cookies"><p><button class="favorite-food-submit">Submit</button></p>
		</div>
	<?php return ob_get_clean();
}

This piece of the code is similar to last week; it registers two WordPress shortcodes to display our “Favorite Food” form. More on shortcodes here and here if that would be helpful.

JavaScript

We’re using JavaScript to do quite a bit. We’ll discuss the two most important things:

Ajax Request

/* Ajax request to get current cookie despite caching */
$.post(
	ajaxUrl.url, 
	{
		'action': 'wpshout_get_fave_food_cookie',
		'cookie': cookieName,
	}, 
	function( response ) {
		console.log( response );
		fave = response;
		if( typeof( fave ) === 'undefined' ) {
			$( '.current-favorite' ).html( noFaveText );
		} else {
			$( '.current-favorite' ).html( faveText + fave );
		}
	}
);

This is the crux of this solution. Rather than ask the current page for what’s stored in $_COOKIE our JS makes a direct request to WordPress’s AjaxURL instead. The result is that we retrieve uncached results from the server.

When it gets the result, it publishes that result to the page, as the contents of the element with class current-favorite.

Again, if Ajax itself is unclear, please check out our earlier tutorial on the subject.

Updating Through Form

/* Update favorite food through form */
$( '.favorite-food-submit' ).click(function() {
	cookie.remove( cookieName );

	cookie.set( cookieName, faveTyped, {
	   expires: 999, // Expires in 999 days
	   path: '/',
	});
			
	fave = cookie.get( cookieName );
	$( '.current-favorite' ).html( faveText + fave );
});

This code is what lets us set the cookie to a value specified by the user. Note that we don’t need to use Ajax for this piece: our problem isn’t setting things to $_COOKIE, it’s retrieving the new values despite caching.

To set the cookie itself, we’re first removing the old value with cookie.remove(), and then setting a new value with cookie.set(). Finally, we’re updating the form on the current page load with cookie.get() and jQuery. All three of these cookie. methods come from the cookie.js library, which simplifies working with cookies in JavaScript.

Cache Busted, Cookies Delicious!

Well! I hope this gives you a sense of how to work with cookies in a cached environment. You do have to know some Ajax, but the beautiful thing about it once it works is that it’s not only cache-proof, but also dynamic—no browser refreshes needed.

If you have any questions or thoughts, we’d love to hear them in the comments below. Thanks for reading!

Image credit: Darren Tunnicliff


3 Responses

Comments

  • Andrew says:

    Thanks for the powerful article. Any suggestions on how to create shortcodes that could be used to wrap content in ajax (in posts and in wp templates)? This way, shortcodes could be used to prevent content, forms, etc from being cached. Thanks for any suggestions.

Pingbacks