TUTORIAL: Using WordPress’ Ajax API

WordPress has tons of APIs to do tons of things. It really does. One of the cool ones that I’ve been using a lot lately, has been around for a bit. It’s the Ajax API. Sure, you could write your own Ajaxy thing but why do that when WordPress lets you do it all very simply.

This tutorial will show you how to replicate the functionality that is presented in the “Like” button at Facebook. It assumes that you have WordPress set to require users to be logged in to take such actions. It wouldn’t make much sense for someone to not be logged in for such functionality. Obviously, you could do something else with Ajax (polls? Loading new content?) that doesn’t have the same assumption, but in this case… we’re assuming a user is logged in and the $current_user global is set.

Getting Started

Let’s start with the basic PHP class (you do write good object-oriented code, right?)

1
2
3
4
5
6
7
8
class My_Like_Button {

    function __construct()
    {
    }
}

$my_like_button = new My_Like_Button;

This code is just a basic skeleton that provides a constructor (the __construct() method, which I’ll use later. It also instantiates a new My_Like_Button object and assigns it to the $my_like_button variable for use in the global scope. Obviously, you can use whatever strikes your fancy for the class name or the global variable.

Building the HTML for the Like Button

Next, we need the basic HTML structure that will be used in our theme for the Like button. For the sake of simplicity, I’m not providing any CSS, but feel free to do as you wish. We need to add this as a new method in our class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function like_button()
{
    if( !is_user_logged_in() )
        return false;
       
    global $current_user;
       
    $html = '<form action="" method="post">
        <input type="hidden" name="like_post_id" id="like_post_id" value="'
. get_the_ID() .'">
        <input type="hidden" name="like_user_id" id="like_user_id" value="'
. $current_user->ID . '">
        <input type="button" name="like_this_post" id="like_this_post" value="Like" />
    </form>'
;
    return $html;
}

This form provides two hidden form fields using get_the_ID() and the $current_user object. Having done this (and after ensuring there is a logged in user), we can now drop this into our theme in the Loop as a template tag:

1
<?php echo $my_like_button->like_button(); ?>

Sending the Ajax Request with jQuery

This is useless by itself. We need something to handle the Ajax. But before we get there, we need to understand how Ajax works within the WordPress context.

All requests must go to wp-admin/admin-ajax.php. Forget the confusion about why you would send a front-end Ajax request to the back-end. You just do.

On the back-end, you’ll have to define functions that will hook into the Ajax API to let WordPress know how to handle the request. More on that in a minute. Let’s build some jQuery to send this to admin-ajax.php.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function js()
{
    wp_enqueue_script( 'jquery' );
    wp_print_scripts();
    ?>
    <script>
    jQuery(document).ready(function(){
           
        jQuery('#like_this_post').click(function(){
            var like_post_id = jQuery('#like_post_id').val();
            var like_user_id = jQuery('#like_user_id').val();
           
            jQuery.post(
                '<?php echo get_option('siteurl') . '/wp-admin/admin-ajax.php' ?>',
                {
                    action      : 'register_like',
                    user_id     : like_user_id,
                    post_id     : like_post_id,
                    _wpnonce    : '<?php echo wp_create_nonce('nonce-register_like'); ?>',
                },
                function(response) {   
                    alert(response);
                }
            );
               
        });
    });
    </script>
    <?php
}

This is just a PHP function that we can use to throw some jQuery (using the .post() method for our Ajax call) into the head. Note the nonce. You must include this for security. Additionally, you must have an action set. The action is used by WordPress to route the Ajax request.

Finally, we’re passing two extra pieces of data – user_id which is the WordPress user ID and the post_id which is the WordPress post ID of the current $post object inside the Loop. Both of these are supplied in the hidden form fields we created earlier.

Let’s add a new method for hooks and call it in our constructor, so that hooks are executed when the object is instantiated:

1
2
3
4
5
6
7
8
9
function __construct()
{
    $this->hooks();
}
   
function hooks()
{
    add_action( 'wp_head', array( $this, 'js' ) );
}

Supplying the Ajax Handler

Having hooked the jQuery into WordPress, we now need something on the other end to handle the request and return something back to our page. For this, let’s create a method called handle_like().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function handle_like()
{
    global $wpdb;
    $user_id = (int) $_POST['user_id'];
    $post_id = (int) $_POST['post_id'];
       
    if( !is_user_logged_in() )
        return false;
       
    if( !wp_verify_nonce( $_POST['_wpnonce'], 'nonce-register_like' ) )
        die( 'Go away, asshole!' );
       
    /*
        Here is where we do some sort of database operation to associate
        the Like of the given post with the user who performed the action
       
        Make sure you check for errors. In order to return data, you must
        echo something that the originating page can see. True or false
        only makes sense on this page and not back there.
           
        Typically, you'd output some sort of JSON, XML or plain text.
    */

    $meta_id = add_post_meta( $post_id, 'user_liked', $user_id );
    if( !$meta_id )
        echo "Like not recorded";
    else
        echo "Like recorded";
    exit;
}

In this method, it’s important that all data be sanitized to avoid security problems. I’m performing the most basic of data security here by casting the $_POST variables for the users ID and the post ID to integers.

We’re also checking to make sure the user is logged in. This is why using the built in WordPress Ajax API is actually kind of important. If you build your own Ajax handler, there’s no way to reliable tie in to WordPress to use other APIs like the User API.

Finally, make sure you verify the nonce so you know the request is coming from a legitimate source.

The three security procedures I just listed will largely prevent SQL Injection, ensure authenticated users and eliminate the possibility of Cross Site Request Forgery (CSRF) and generally should be used in all WordPress plugin development.

Simply though, the above method takes the post ID, the user ID and creates a new record in the postmeta table. On success, it will print a success method readable by the original sending page and vica versa on failure.

Note: You must use die() or exit() at the end of the method to make sure the Ajax response exits properly.

Tying the Handler into WordPress

Now that we have a handler method to do our bidding, we need to make sure WordPress knows about it so it routes Ajax requests to the proper place. To do this, we leverage the wp_ajax_{$action} hook.

Remember earlier, while building the jQuery Ajax request, I mentioned that an action POST variable had to be set? WordPress looks to this variable to create a dynamic hook that we can use our new Ajax callback to hook into.

1
add_action( 'wp_ajax_register_like', array( $this, 'handle_like' ) );

Because our action in the js() method was set to register_like, the hook WordPress creates dynamically is wp_ajax_register_like and we can use that to hook our Ajax handler onto.

Wrapping it all Together

Easy right? Use jQuery to send data, wp-admin/admin-ajax.php to receive the Ajax request and pass it on to an appropriate handler function, and send data back to the original page. On the receiving end, you can use normal means to handle the returned data as you wish.

You can download the full code used here at Github.

skunkworks

Skunkworks

Back in October, I announced my departure from WP Engine. At that time, though I didn’t talk about it on this blog, I decided to take some time off, more or less. Since 2006, I’ve been hard at work with very little time alotted to myself. I spent 2 years with b5media and jumped immediately into a failed role at Lijit (who has now been acquired by Federated Media – nice work, guys!). Upon my departure from Lijit, shortly after the market bottom in 2008, and needing money desperately, I went into full time WordPress consulting. I did that until I moved to Austin last year when I went in full time on WP Engine and stayed there for 15 months or so.

So basically, I haven’t had a lot of time to myself. So I took time. In the past few months, I’ve taken some large consulting contracts, but mostly, I’ve spent time travelling to Maryland, Seattle, Chicago – all for pleasure, nothing for work. I’ve spent time trying to weigh my priorities and wants. I’ve tossed around starting up a new company or doing something different.

At the end of the day, now that it’s 2012, I know what I want to do. It’s a bit unusual, but I think it’s important and can really revolutionize a boutique agency. I’m not comfortable doing social media work. I’m too honest and raw in my own online presence and many companies and clients may not be comfortable with my level of authenticity.

I also don’t want to do what is common among agencies – sweatshop site development. Hey, no offense. That’s what it is. Take on 30 new clients, promise them websites that are the brainchilds of the agency marketing “expertise” and ask the developers to crank them out with little to no strategic or creative input.

That might work for some developers, but I’m not a normal developer. I’m a highly established WordPress professional that has commanded 5 figure consulting deals, written a 700+ page book on the subject and have built some of the most complex WordPress solutions I’ve ever seen (Humble brag! Also proprietary, but can provide in person demos). I’m not a good fit for sweatshop site generation.

You know what agencies need that no one is doing because no one has taken the time to think outside the box? A skunkworks division. What agencies need to differentiate themselves from the thousands of other agencies they are competing with is a person or small group of people with autonomy and who are focused soley on creating disruptive technologies that no one else is doing. Try things. Fail at some, succeed at others. Test market demand. Offer exclusive access to stuff that no other agency has. Innovate, innovate, innovate.

That’s what I want to do. And someone sees the sense in that. And someone realizes that that is worth thinking outside the box for. Someone is willing to invest in that competitive advantage. Someone gets it. It’ll take money. It’ll take risk. It’ll take balls of steel. Or you can be normal. Who wants to be normal?

So as we enter 2012, I am open to conversations around this or other creative outlets you might want to explore if you want a competitive advantage. Email me at aaron@technosailor.com.

TUTORIAL: Adding an oEmbed Provider to WordPress

I don’t often write tutorials. I probably should. But normally it’s only when someone asks me something and I think, “Hey, self… you should write up how to do this”. As if a book wasn’t enough.

Last night I was at the Austin Web Holiday party, a gathering of some 15+ technical meetup groups cross-pollinating over beer and socializing. I was introduced to one guy (can’t remember his name!) who had built a video site and enabled it for oEmbed. He couldn’t understand why WordPress wouldn’t just automatically let users use his videos, like it does for YouTube, Vimeo, etc. The full list of default oEmbed providers are listed here.

WordPress doesn’t allow automatic use of oEmbed for security reasons. Otherwise, someone could build a video service stuffed with malicious code that could potentially access your database or create a man in the middle attack or worse. WordPress.com certainly doesn’t allow arbitrary oEmbed sites and the dot-org open source software doesn’t allow arbitrary stuff automatically. But it can be done, on the dot-org side, with a plugin. All it is a hook.

Here’s an example. If you want to register an oEmbed video site that is, say, at (randomly) http:/mysuperawesomevideosite.com and your videos are of the format http://mysuperawesomevideosite.com/video/*, it’s as simple as adding a function in your plugin (or more properly from a PHP perspective, a method in a class – but that’s a personal preference. The method/function should call the  wp_oembed_add_provider() function.

In it’s simplest form, all you have to do is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class My_Plugin {

  var $oembed_endpoint;
  var $oembed_format;

  function __construct()
  {
    $this->oembed_endpoint = 'http://mysuperawesomevideosite.com';
    $this->oembed_format = 'http://mysuperawesomevideosite.com/video/*';

    $this->new_oembed();
  }

  function __destruct() {}

  function new_oembed()
  {
    wp_oembed_add_provider( $this->oembed_format, $this->oembed_endpoint );
  }

}

Then, to make this code work, just instantiate the class somewhere.

1
$my_plugin = new My_Plugin;

Voila!