Getting Started
To begin, we need a plugin. Something simple would work; we should name it in respect to its purpose, so something like WDS_Shortcode_AJAX_Handler. We’re going to need two methods in the class–one to hook into WordPress and one to handle the AJAX.
<?php /* Plugin Name: WebDev AJAX Plugin URI: http://# Description: Quick AJAX Tutorial Version: 0.1a Author: Anonymous Author URI: http://# */ class WDS_Shortcode_Ajax_Handler { function hooks(){ } function handle_ajax(){ } } $web_dev_plugin = new WDS_Shortcode_Ajax_Handler(); $web_dev_plugin->hooks();
One of the major advantages of the OOP approach is you only have to uniquely name the class. The class methods can be named whatever and are pretty much self-documenting, which is always a plus.
One thing I’ve learned while at WebDevStudios is to not put your hooks in the construct of a class. The reason for this is that every time the class is initiated, the hooks fire. Therefore, by separating these hooks from the actual construct of the object, we fire them only once to prevent overhead for larger classes.
When I showed this post to Michael, he pointed out that this approach also allows the class to be re-usable elsewhere.
Now that we have the plugin scaffolding ready to go, we need to get WordPress itself in on the action. You do this by creating the JS file to handle our AJAX and queue it up. Start off by creating a folder named “js” in your plugin (if you haven’t already) and we’ll name the file the same as our plugin: webdev.js
I’m going to use jQuery for the purpose of this tutorial, but you do not need to use jQuery for this to work; jQuery is just the lazy-man’s javascript (that, and I’m spoiled on it).
window.webdev_ajax = ( function( window, document, $ ){ var app = {}; app.cache = function(){ }; app.init = function(){ app.cache(); }; app.post_ajax = function(){ }; app.ajax_response = function(){ }; app.form_handler = function(){ }; $(document).ready( app.init ); return app; })( window, document, jQuery );
The layout is a bit overkill for this little ol’ how-to, but it’s what I’m used to.
Just in case you’re not, I’ll explain:
- app.cache method is what will hold our selectors, so we don’t have to re-access them everytime. Eric Mann wrote a great article on jQuery caching, and it’s something we’re accustomed to at WDS.
- app.init is where the initialization will happen, of course, and this method will house our form processing hook, as well as initiate the cache.
- app.form_handler will be where we stop the usual form processing from occuring, and do what we want, in this case, it will be to post in the background.
- app.post_ajax is pretty self-explanatory–it’s where we’re going to post to the PHP file and get the data.
- app.ajax_response is the sibling to post_ajax–we’re going to handle the response from the AJAX there.
The entire project scaffolding is done at this point, so now we can get to the juicy bits.
Adding Hooks
WordPress has hooks for everything! First up, we need to hook into the front-end and queue the js file we just made, as well as setup a few localized things so our script knows what to do. Go ahead and setup the AJAX hooks here, too.
<?php /* Plugin Name: WebDev AJAX Plugin URI: http://# Description: Quick AJAX Tutorial Version: 0.1a Author: Anonymous Author URI: http://# */ class WDS_Shortcode_Ajax_Handler { function hooks(){ add_action( 'wp_ajax_webdev', array( $this, 'handle_ajax' ) ); add_action( 'wp_ajax_nopriv_webdev', array( $this, 'handle_ajax' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_shortcode( 'webdev_tut', array( $this, 'shortcode_output' ) ); } function handle_ajax(){ } function enqueue_scripts(){ wp_enqueue_script( 'webdev_js', plugins_url( '/js/webdev.js', __FILE__ ), array( 'jquery' ), '1.0', true ); wp_localize_script( 'webdev_js', 'webdev', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'webdev' ), ) ); } function shortcode_output( $atts ){ $output = '<form action="" method="POST" class="ajax_tut_form">'; $output .= ' <input type="text" name="ajax_tut">'; $output .= ' <input type="submit" value="submit">'; $output .= '</form>'; return $output; } } $web_dev_plugin = new WDS_Shortcode_Ajax_Handler(); $web_dev_plugin->hooks();
When it comes to AJAX, security is key. The best way to handle this is to use WordPress’ built in nonce features to setup a secure string so we know that our plugin/app is initiating the request. Of course, you can use referrer checking (and many other things), but I’ll keep it simple for now.
At this point we’ve localized the script to allow us to access those object properties from within our JS. This is really important! Without localization of the script, you will not know where your AJAX URL is supposed to be unless you’re on the admin panel. We can localize more variables if we want, say a blog option, or some other setting–just be sure you escape it properly.
For the purpose of this how-to, I also made a quick shortcode, just so our AJAX can do its magic and that we have something to hook onto.
With wp_ajax_ and wp_ajax_nopriv_, they allows us to make AJAX calls both while logged in and logged out, which is what we want for now. Without wp_ajax_nopriv_ unauthenticated users would not be allowed to use our nifty JavaScript, which would be a shame.
Now on to the JavaScript magic!
The JavaScript
When it comes to processing forms, there are a few ways you can do it. I like to serialize the data before I post it, but you can even access each field individually, etc.
window.webdev_ajax = ( function( window, document, $ ){ var app = {}; app.cache = function(){ app.$ajax_form = $( '.ajax_tut_form' ); }; app.init = function(){ app.cache(); app.$ajax_form.on( 'submit', app.form_handler ); }; app.post_ajax = function( serial_data ){ var post_data = { action : 'webdev', nonce : webdev.nonce, serialized : serial_data, }; $.post( webdev.ajax_url, post_data, app.ajax_response, 'json' ) }; app.ajax_response = function( response_data ){ }; app.form_handler = function( evt ){ evt.preventDefault(); var serialized_data = app.$ajax_form.serialize(); app.post_ajax( serialized_data ); }; $(document).ready( app.init ); return app; })( window, document, jQuery );
Let me try and break this down by method:
- app.cache: As stated before, this is our cache method that holds our jQuery objects, and can house global variables as well. For now it’s holding the AJAX form, which we later access in the init method.
- app.init: Initializes the cache and registers a handler for the form’s submission.
- app.post_ajax: Where the posting happens. This is also where we make use of those localized variables from the previous section. Take note: I’m telling the jQuery post method that we’ll be receiving json data (more on that when we get back to the PHP side of things).
- app.ajax_response: This will not be populated until we tell our PHP script what data to send back.
- app.form_handler: This is fired from our app.init method when the form is submitted. We pass the event data into the method and tell the document to stop at that point via the preventDefault method. At this point, we serialize the data (remember there are other ways) and pass that form data to the post_ajax method.
Handling the AJAX in PHP
Now for the magical part of this how-to, and more than likely what you’re reading it for: handling the AJAX. Not many people know about the nifty little WordPress method for sending json data back to an AJAX request with the proper status. Have a look at our handle_ajax method in the plugin:
function handle_ajax(){ if( ! wp_verify_nonce( $_REQUEST['nonce'], 'webdev' ) ){ wp_send_json_error(); } wp_send_json_success( array( 'script_response' => 'AJAX Request Recieved', 'nonce' => wp_create_nonce( 'webdev' ), ) ); }
In this method, you’ll notice two nifty little methods: wp_send_json_error() and wp_send_json_success(). For me, these are the bread and butter of AJAX in WordPress. They also have a sister function called wp_send_json(), but I prefer success or error since they add the object properties for me. If it’s there use it, right?!
In previous versions of WordPress, you would have to do this yourself–encoding into json, sending your own success/error property, and killing your script via DIE…no more!!! These methods do all that nifty stuff for ya! Remember in the previous JavaScript section that I told you that the post is expecting JSON data? That’s where this comes into play–these little gems do that for us!
The first thing we do in the method is verify the nonce we received. If it fails at this point, stop.
If, at this point, we passed that check, then the script will send an array of data back to the JavaScript. Since we used the nonce, we need to update it; I’ve included the nonce array key as well to send a new one so we don’t receive any errors.
Now on to the JS side of things to handle the PHP response:
I can’t stress security enough when working with AJAX calls, it takes just a little bit of work to put in a nonce field and verify it, you have no excuse not to really.
Handling PHP’s Response in JavaScript
This is a little simpler and straightforward. Here, we receive the response from PHP and since we used the _error and _success methods from WordPress, we know that we should receive a success property.
app.ajax_response = function( response_data ){ if( response_data.success ){ webdev.nonce = response_data.data.nonce; alert( response_data.data.script_response ); } else { alert( 'ERROR' ); } };
With _success we will receive success=true in the json response, and we can use that to see if we got an adequate response or not. Since in the first section of the conditional we received a success value, we need to reset the nonce value. Otherwise, if the AJAX request was not a success, throw an error.
At this point, you can do WAY more. Swap out the alerts for some custom popups, do some nifty form processing on the PHP side of things for that shortcode, change some text on the page–there are endless possibilities!
That’s it, folks! I would be interested in hearing your preferred methods, and if you have a better way of doing it, by all means, let me know!
If you would like the full how-to source code I created a quick repo, feel free to fork or download it and tinker with it: get the source.