Binding PHP Functions to WordPress's Ajax Handler

By Rob Gravelle

As we saw in Referencing WordPress's Native Ajax Handler in JavaScript, Ajax requests go through the wp-admin/admin-ajax.php script. In that article, we learned how to add our JavaScript files to the page using the wp_enqueue_script() and wp_localize_script() functions. Today's follow-up will describe how to hook your server-side callbacks to the admin-ajax.php script.

The Role of the functions.php File

The functions file is a lot like a WordPress Plugin in that it adds features and functionality to your WordPress site. It's the place from which to call both PHP and built-in WordPress functions, and to define your own functions. It resides in your theme's folder, under "wp-content/plugins", so it adds behaviors that only apply to the current theme. One of the things that you can do in the functions.php file is hook into WordPress's collection of actions and filters that can affect almost everything WordPress does.

The add_action() Function

Your theme's custom actions are defined in the functions.php file using the add_action() function. It accepts two mandatory parameters and two optional ones:

add_action( $hook, $function_to_add [, $priority][, $accepted_args] );

Let's take a moment now and see what each parameter does.

  • $hook - type: string
    The name of the action to which the callback function (the $function_to_add argument) is hooked. This name must match an entry in WP's action hooks list. It can also be the name of an action inside a theme or plugin file, or the special tag "all", which causes the function to be called for all hooks.
  • $function_to_add - type: callback function.
    The name of the function you want to $hook in.
  • $priority - type: int
    An optional argument for specifying function execution order. The lower the number, the earlier the execution. Functions with the same priority are executed in the order in which they were added to the action. When omitted, the $priority defaults to 10.
  • $accepted_args - type: int
    Another optional parameter that defines the number of arguments the hooked function accepts. As of WordPress 1.5.1, hooked functions can accept extra arguments, which are set when the do_action() call is invoked. When omitted, the $accepted_args defaults to 1.

The add_action() function always returns true.

The wp_ajax_(action) Hook

Two of the many action hooks available to us are the wp_ajax_(action) and wp_ajax_nopriv_(action). Both allows you to create custom handlers for your own AJAX requests by substituting your AJAX request's $hook action property for the "(action)" part of the name. The two hooks are identical, except for one key difference: wp_ajax_(action) executes for users that are logged in while wp_ajax_nopriv_(action) executes for users who are not logged in. That makes it easy to configure different callbacks for users who are logged in and those who aren't. Should you wish to hook the same Ajax callback for both visitors and logged-in users, you can do something like this:

add_action('wp_ajax_my_action', 'my_action_callback');
add_action('wp_ajax_nopriv_my_action', 'my_action_callback');

Returning Data From Your Ajax Function

Note that the callback function that you are hooking into WordPress is for the server-side handling of Ajax requests and NOT the JavaScript callback. That's a function that is assigned on the client-side. This function handles the request on the server and returns data to the browser. Here's a function that retrieves restaurant menus using IDs passed in via the $POST array. A foreach loop iterates over each ID in the array and adds the menu data to the $new_menus array. Finally, the header is sent to prepare the browser for json-encoded data, the data is encoded and sent, and exit is called. That last part is important! By adding exit() or die() after returning your desired content prevents the default response from admin-ajax.php - usually zero (0) - being returned as well:

function myajax_update_cached_menus() {
    $restaurantData = $_POST['data'];

    $new_menus = array();
    foreach ($restaurantData as $id => $value) {
        $menu = array(
            "metaData" => new restaurant_data( $id ),
            "menus"    => restaurant_data::get_menu_download_data_parent( $id )
        );
        $new_menus[$id] = $menu;
    }

    // response output
    header( "Content-Type: application/json" );
    echo json_encode($new_menus);  

    // IMPORTANT: don't forget to "exit"
    exit;
}
// if both logged in and not logged in users can send this AJAX request,
// add both of these actions, otherwise add only the appropriate one
add_action( 'wp_ajax_nopriv_myajax-update-cached-menus', 'myajax_update_cached_menus' );
add_action( 'wp_ajax_myajax-update-cached-menus', 'myajax_update_cached_menus' );

For completeness, here is the client-side code. Pay particular attention to how the action matches the "(action)" part of the add_action()'s $hook action argument above:

<div class="clear" id="divToggleFavoritesPadding"></div>
<div id="divToggleFavorites"><h3 id="toggleFavoritesHeader"><a id="toggleFavorites" href="JavaScript: getOutOfDateMenus();">View Favorites</a></h3></div>
<div id="results" style="color: white; font-size: 1.2rem; margin: 10px; height: 100px;"></div>
<script language="JavaScript" type="text/javascript">
function getOutOfDateMenus() {
  var restaurantMetaData = getRestaurantMetaData();
  if (!restaurantMetaData.length) {
      //no menus saved
      results.html('You have no saved menus.');
      return;
  }
 
  jQuery.post(
	  RestaurantData.ajaxUrl,
   	{
       		// here we declare the parameters to send along with the request
       		// this means the following action hooks will be fired:
       		// wp_ajax_nopriv_myajax-submit and wp_ajax_myajax-submit
       		action : 'myajax-submit',

       		// other parameters can be added along with "action"
       		data: restaurantMetaData
    	},
    	function( res,code,xhr) {
           if (typeof res == 'string') {
                results.html(res); 
           }
           else if (restaurantMetaData.length) { 
               var outOfDateMenus = res;
               content.html('');

               for (var i=0; i<restaurantMetaData.length; i++) {
                   //build the list…
               }
               //show the list
               content.append(list).animate({opacity: '1'}, 'slow');
               content.fadeTo( 1000, 1 );
               toggleFavorites.html("Hide Favorites");
            }
            else {  
                results.html('You have no saved menus.');
            }
    	}
   ).fail(function() {    
	     results.html('An Ajax error occurred.' );  
   });
}
</script>

Conclusion

Just about any behavior that you want to implement in WordPress can be done via the functions.php script. Ajax is certainly no exception. Just hook your server-side callback function via the add_action() function, using the wp_ajax_nopriv_(action) and wp_ajax_(action) hook names. Then pass an action parameter of the same name from the client-side Ajax call. Like a former co-worker of mine used to say: "it should just work!"


If you enjoyed this article, please contribute to Rob's rock star aspirations by purchasing one of Rob's cover or original songs from iTunes.com for only 0.99 cents each.

Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleWebDesign.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses. Email Rob to receive a free estimate on your software project.

In his spare time, Rob has become an accomplished guitar player, and has released several CDs. His band, Ivory Knight, was rated as one Canada's top hard rock and metal groups by Brave Words magazine (issue #92).

Rob uses and recommends MochaHost, which provides Web Hosting at $1.95 per month, 2 LifeTime Free Domains, and 6 Months Free!



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date