Build your Own WordPress JSON API Controller

By Rob Gravelle

The JSON API plugin for WordPress is what I like to call an oldie but a goodie. It hasn't been updated in some time, but the JSON API plugin is still a great tool for making data from your WordPress database available as a JSON feed. It comes with a lot of really useful core functions, some of which we saw in the Fetching Post Data using the WordPress JSON API Plugin article. Moreover, the plugin is also extensible, allowing us to append our own RESTful JSON API methods to the JSON API core controller. In today's article, learn how to create a custom controller to handle the retrieval of data from the WordPress database, meta fields, and even from other feeds.

A Basic Controller Class

The following class is based on one that I wrote for a restaurant information site. For the purposes of this article, we'll call it "GFT".

<?php

/*
 Controller name: JSON_API_Gft_Controller
 Controller description: JSON API GFT Controller
 */

final class JSON_API_Gft_Controller {

  public function info() {
    return array(
      'version' => '1.0'
    );
  }

  public function menus_for_restaurant() {
    return array(
      'menus' => array( "Sample menu 1", "Sample menu 2", "Sample menu 3" )
    );
  }
}

The name of the controller class must follow the pattern JSON_API_[name]_Controller where [name can be whatever we want]. The public methods of our controller will be the endpoints of our API. Each public method should return an array of JSON encodable objects. Our class has two public endpoints: info and menus_for_restaurant. The info() function is not required but may be included if you want to provide some information about your plugin. The menus_for_restaurant() function is one that we will call to fetch an array of menu objects.

Registering Our Controller

In order for the JSON API to be aware of our new controller, we need to to do two things inside of our functions.php file:

  1. Register it with the other controllers.
  2. Set its path on the sever.

The first step is accomplished using the json_api_controllers filter. That's where we assign a name to our controller. That name may be included in the path filter so that "json_api_[name]_controller_path" becomes "json_api_gft_controller_path". Our code adds our controller to our main theme folder.

function add_gft_controller($controllers) {
  $controllers[] = 'gft';
  return $controllers;
}
add_filter('json_api_controllers', 'add_gft_controller');

function set_gft_controller_path() {
  return get_stylesheet_directory() . "/gft.php";
}
add_filter('json_api_gft_controller_path', 'set_gft_controller_path');

Once that's done, you should see the controller in the list under Settings -> JSON API.

json_plugin_controllers.jpg

Calling Our Controller Methods

Calling our own functions is not much different than those of the core API, except that we need to include the name that we appended to the $controllers array ("gft") before the function name ("menus_for_restaurant"). Here then is the URL to call menus_for_restaurant():

http://therestaurantsite.com/api/gft/menus_for_restaurant/

That returns the following data (formatted for readability):

{
  "status": "ok",
  "menus": [
    "Sample menu 1",
    "Sample menu 2",
    "Sample menu 3"
}

Accepting Input Parameters

Right now our menus_for_restaurant() function is a little too simplistic because it doesn't accept a restaurant ID whose menus we want to fetch. One way to fix that problem is to accept a restaurant_id query parameter, for instance:

http://therestaurantsite.com/api/gft/menus_for_restaurant/?restaurant_id=5150

We can interact with the JSON API by including the global $json_api object in our methods. Query parameters are stored in the $json_api->query object. We can use it to check for our restaurant_id parameter, validate its value, and throw an exception if either check fails.

public function menus_for_restaurant() {
    global $json_api;
    global $wpdb;
   
    if (!$json_api->query->restaurant_id) {
       $json_api->error("Missing 'restaurant_id' parameter.");
    }
   
    $restaurant_id = $json_api->query->restaurant_id;
   
    if (!is_numeric($restaurant_id)) {
       $json_api->error("Invalid 'restaurant_id' parameter.");
    }

Fetching Associated Menus

Menu IDs are linked to restaurant posts in the wp_postmeta table using a meta_key of "menu_parent". Our controller can use any of the existing WordPress functions to collect data, but JSON API also includes an introspector that may be utilized to fetch various Response objects including Post, Category, Tag, Author, Comment, and Attachment. The introspector's get_posts() method accepts an array of arguments that tailor the results. In particular, the post__in (array) specifies which posts to retrieve by ID. The posts_per_page parameter defaults to ten, so if you want to fetch all of the associated posts, you must supply a sufficiently larger number.

$menu_ids = $wpdb->get_col( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'menu_parent' AND meta_value = $restaurant_id");     
if ($menu_ids) {
    $args = array(
      'post_type'      => 'menu'
     ,'post__in'       => $menu_ids
     ,'orderby'        => 'post__in'
     ,'posts_per_page' => 100
    );
    $menus = $json_api->introspector->get_posts( $args );
}      
else {
    $json_api->error("No menus found for 'restaurant_id' " . $restaurant_id . ".");
}

return array(
  'menus' => $menus    
);

Here is the full source for our controller:

/*
 Controller name: JSON_API_Gft_Controller
 Controller description: JSON API GFT Controller
 */

final class JSON_API_Gft_Controller {

  public function info() {
    return array(
      'version' => '1.0'
    );
  }

  public function menus_for_restaurant() {
    global $json_api;
    global $wpdb;
   
    if (!$json_api->query->restaurant_id) {
       $json_api->error("Missing 'restaurant_id' parameter.");
    }
   
    $restaurant_id = $json_api->query->restaurant_id;
   
    if (!is_numeric($restaurant_id)) {
       $json_api->error("Invalid 'restaurant_id' parameter.");
    }

    $menu_ids = get_associated_menu_ids($restaurant_id);       
    if ($menu_ids) {
        $args = array(
          'post_type'      => 'menu'
         ,'post__in'       => $menu_ids
         ,'orderby'        => 'post__in'
         ,'posts_per_page' => 100
        );
        $menus = $json_api->introspector->get_posts( $args );
    }  
    else {
        $json_api->error("No menus found for 'restaurant_id' " . $restaurant_id . ".");
    }

    return array(
      'menus' => $menus    
    );
  }
}

Conclusion

The JSON API plugin's extensibility allows us to append our own RESTful JSON API methods to the JSON API core by registering additional custom controllers. In today's article, we learned how to invoke methods of the JSON API's introspector object to retrieve posts for an array of IDs.


Rob Gravelle

Rob Gravelle resides in Ottawa, Canada, and is the founder of Gravelle Web Design. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses.

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) and reached the #1 spot in the National Heavy Metal charts on Reverb Nation.



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