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:
- Register it with the other controllers.
- 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.
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.