Development

Handling AJAX in WordPress in Style

AJAX (Asynchronous Javascript and XML) isn’t something that should be feared by anyone. It’s insanely powerful, versatile, and can provide your users cool experiences like processing forms without page redirection, dynamically loading posts while a user scrolls the page, and much more. Most of all, WordPress has a few nifty gems up its sleeve.
In this post, I’ll show you how I handle AJAX in WordPress:

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.

Comments

10 thoughts on “Handling AJAX in WordPress in Style

  1. Hi Jay,

    There are two points in your article which could be misleading. We’re all here to learn, so I hope you don’t mind me pointing them out!

    The code in the “When it comes to AJAX, security is key” section suggests that checking a nonce is sufficient security protection for an AJAX request. This is not the case, and for any action which should only be performed by a logged in user (or a user with a certain capability), you should also perform a capability check using current_user_can(), either before or after the nonce check.

    In your enqueue_scripts() method, you unconditionally output a nonce for the key webdev. This nonce is output for all users, regardless of whether they’re logged in or not.

    In your handle_ajax() method, you check the nonce but fail to check for permission to perform the action. This means that any user to the site can make that AJAX call and it will happily execute. In the case of your example code it’s not important, but it’s a very good precedent to set, especially in a section titled “When it comes to AJAX, security is key”.

    The reason I point this out is because several popular plugins in recent months have been found to contain vulnerabilities in their AJAX calls, because the plugin unconditionally outputs nonces in the page and then in the AJAX handler it performs a nonce check but doesn’t perform a capability check too.

    The important thing to note is that a nonce only verifies intent, not permission.

    Secondly, your point about “using” a nonce and having to return an updated one is a little misleading.

    Since we used the nonce, we need to update it.

    Nonces in WordPress are time-based and can be reused for the duration that they remain valid (up to 24 hours). They don’t get “used” once you’ve used them once (an important point for developers to be aware of).

    Typically there’s no need to return a refreshed nonce in the AJAX response. However, if you have a page which may remain open for longer than 24 hours and you need to ensure the nonce still works, you should set up a separate, recurring AJAX call which only polls for a fresh nonce, or you can use WordPress’ Heartbeat API to do so. Simply returning a fresh nonce in the response wouldn’t be effective in this situation because the nonce that it sends in the first place could be invalid if a call hasn’t been made for more than 24 hours.

    John

    1. @John:
      I definitely agree that a permission-based ajax request should in fact check the user’s capabilities before performing the transaction, and return the proper _success or _error response. You do not always need a capability/perm check to complete an ajax request if your AJAX handler is coded properly, infinite scroll is a great example, as well as some light-boxes or lazy-loaders.

      I completely forgot about the nonce timeouts. I swear I remembered a time when nonces lasted only until they were used, though may have been on a different CMS, so thanks a million for pointing that out, saves me some time in the future 🙂

  2. Nicely explained, but how do we implement it?

    I want to get data from a php file from a wordpress page?
    You have not explained this!!!

    1. It’s by shortcode, see the line:
      add_shortcode( ‘webdev_tut’, array( $this, ‘shortcode_output’ ) );

  3. Please how did you get the webdev object on the javascript side from which you took nonce and ajax_url values from?

    1. John,

      That’s where the wp_localize_script() function came into play in the ‘Adding Hooks’ section of the article.

      wp_localize_script() takes a handle, name, and data variable which you can see here: https://codex.wordpress.org/Function_Reference/wp_localize_script

      The ‘name’ variable is what will be used in the javascript side of things. In this case, webdev. The handle is the handle of the registered script, and the data is a key value paired array of data.

  4. I understand that this article has been out there for a while…

    I’m having trouble with AJAX on the front end of a WordPress plugin I’m developing.

    I need access to my class variables based on the value set in display_func

    Is this possible??

    Any thoughts?

    class example_class {
    private $options = array(); //(array)get_option(KEY); … where KEY is valid
    public function __construct() {
    add_action( ‘wp_ajax_nopriv_get_value’, array( $this, ‘get_value_handler’) );
    add_action( ‘wp_ajax_get_value’, array( $this, ‘get_value_handler’) );
    add_shortcode(‘mycode’, array( $this, ‘display_func’ ));
    }

    function display_func( $atts, $content ) {
    $this->options = (array)get_option(KEY); // Note Key depends on the short code parms
    }

    function get_value_handler() {
    $nonce = $_REQUEST[‘ajaxNonce’]; // and verify, etc

    echo “options: ” . $this->options;
    // at this point $this->options is set to whatever is in the constructor.
    // since the options value depends on short code options, I load them in display_func
    }
    }

    1. Byron,

      In order to set the value you’ll need to call the shortcode’s display_func() method. However, in order to do that, you’ll need to know the $atts and $content as well. I’m assuming you’re firing the AJAX from within your shortcode? If that’s the case you may consider creating a ‘helper’ in your shortcode to pass your attributes to the AJAX handler.

      A super simple way to do this would be to json_encode() the $atts parameter of the display_func() like so: $json_atts = json_encode( $atts );

      Then, when you make your post in your ajax, just make use of that in some way, posting it to your get_value_handler(). Once in there, you need to simply decode it: $decoded_atts = json_decode( $_REQUEST['atts'] );
      To give you the general idea, I took a few minutes this morning to make a gist for ya: https://gist.github.com/JayWood/aa8d93b0ebca91e355c2

      1.) This is how you’ll get the json data into your shortcode and ready for process ( there are of course other ways, but this is the simplest ) https://gist.github.com/JayWood/aa8d93b0ebca91e355c2#file-example_class-php-L21
      2.) Now get the data attribute in javascript ( the encoded data ). https://gist.github.com/JayWood/aa8d93b0ebca91e355c2#file-example_class-js-L31
      3.) Send the encoded data over with any other data in your ajax request https://gist.github.com/JayWood/aa8d93b0ebca91e355c2#file-example_class-js-L35
      4.) Decode the data key you sent over in the ajax request into a PHP array or object https://gist.github.com/JayWood/aa8d93b0ebca91e355c2#file-example_class-php-L33 ( see json_decode for more options )

      And there you have it, you now have your shortcode attributes available when processing your ajax request. Hope that helps & happy coding!

  5. I’m new to nonces but trying to implement on my forms.

    I use localize script to set it up..

    wp_localize_script( ‘my-ajax-request’, ‘MyAjax’, array(
    ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ ),
    // generate a nonce
    ‘postCommentNonce’ => wp_create_nonce( ‘myajax-post-comment-nonce’ ),
    )

    In my JS I use the following process…

    jQuery.post(the_ajax_script.ajaxurl, jQuery(“#theForm”).serialize()
    ,
    function(response_from_the_action_function){
    // Return the response and JSON parse to access the array
    $returntext = JSON.parse(response_from_the_action_function); etc…

    However, the parsed JSON is not then correct as the nonce seems to mess it up and I then I can’t seem to check my nonce anyway.

    Probably have the wrong end of the stick and I’m unsure how it relates to what you have brilliantly outlined above.

    Any help would be useful…

    1. Not exactly sure why you’re doing JSON.parse, the basic ajax handshake doesn’t require you to really do that.

      Your response from the AJAX handler ( PHP ) is already good to go in your ajax response value, in this case ‘response_from_the_action_function’, hopefully this can explain it better:
      https://gist.github.com/6366b64a194004a8e64c63e9e5515719

      Now, to get your nonce value INTO your ajax call, I usually cheat and drop it in the form tag, but in your case, you need to modify your data field a bit since you’re localizing it instead, something like so: https://gist.github.com/61d98ac12fb3fa0bf4b224ea4aca0f49

      Also, don’t be afraid to drop some console.logs in there to see what the data looks like. For some sweet snippets check this out: https://webdevstudios.com/2015/10/15/debugging-wordpress-tips-snippets/

Have a comment?

Your email address will not be published. Required fields are marked *

accessibilityadminaggregationanchorarrow-rightattach-iconbackupsblogbookmarksbuddypresscachingcalendarcaret-downcartunifiedcouponcrediblecredit-cardcustommigrationdesigndevecomfriendsgallerygoodgroupsgrowthhostingideasinternationalizationiphoneloyaltymailmaphealthmessagingArtboard 1migrationsmultiple-sourcesmultisitenewsnotificationsperformancephonepluginprofilesresearcharrowscalablescrapingsecuresecureseosharearrowarrowsourcestreamsupporttwitchunifiedupdatesvaultwebsitewordpress