-
Notifications
You must be signed in to change notification settings - Fork 21
JSON API Internals
The internals of the JSON API, written in PHP5, and made to as seamlessly as possible integrate with wordpress and WooCommerce, are structured to ease and abstract out the details of adding in new features to The API.
The Plugin is for the most part separate from The API, that is most of The API Classes and methods do not specifically depend on The Plugin aspect. The API can actually be used by other plugins to facilitate interaction with WooCommerce as they have little dependence on anything taking place within The Plugin.
Things begin in woocommerce-json-api.php in the function called woocommerce_json_api_initialize_plugin()
where we include the filters and actions.
You will see in woocommerce_json_api_activate()
that we have created something called $helpers->getPluginPrefix() . '_slug'
which is equivalent to woocommerce_json_api_slug as the call $helpers->getPluginPrefix()
is a helper method from the underlying framework for plugins used by Red(E). We tend to make code portable and abstract, so that we can just plop some files in to get a skeleton going and have all the stuff we need at our fingertips. This doesn't seem to be the usual method with most Wordpress Plugins, but we like it and don't foresee changing it in the future.
Each user that has a valid Token set, will have a meta entry in the db that is a serialized array with the key woocommerce_json_api_settings
. You can get this info out of the system by using $meta = maybe_unserialize( get_user_meta( $user_ID, 'woocommerce_json_api_settings', true ) );
which will allow you to access things like their Token, what IP Addresses they are allowed to connect from, as well as their API permissions.
Before the settings form is actually rendered, the Array of fields is filtered as such: $attrs = apply_filters('woocommerce_json_api_settings_fields', $attrs);
Adding a new field to The Plugin is as easy as hooking into this filter. The saving of the form is dynamic, so anything to add to these fields, you won't need to worry about them being saved.
<?php
$field = array(
'name' => $helpers->getPluginPrefix() . '_settings[can_' . $method . ']',
'id' => 'json_api_can_' . $method . '_id',
'value' => $helpers->orEq($meta,'can_' . $method, 'yes'),
'type' => 'select',
'options' => array(
array( 'value' => 'yes', 'content' => __('Yes','woocommerce_json_api') ),
array( 'value' => 'no', 'content' => __('No','woocommerce_json_api') ),
),
'label' => __( 'Can access ', 'woocommerce_json_api' ) . ucwords(str_replace('_',' ', $method)),
'description' => __('Whether or not this user can access this method', 'woocommerce_json_api' )
);
The above illustrates making a select box, which is our preferred method for yes/no selections, instead of a check box, which are ugly and cumbersome.
See the file woocommerce-json-api-core.php and the function woocommerce_json_api_settings_page()
the settings method described for The User Settings applies here, with the filtering like so $attrs = apply_filters('woocommerce_json_api_sitewide_settings_fields', $attrs);
with the caveat that these are individual options, for instance, get_option( $helpers->getPluginPrefix() . '_enabled' )
i.e. woocommerce_json_api_enabled is a single option, instead of a serialized array. When adding fields to this form, you should keep this in mind, thusly naming your options like woocommerce_json_api_sitewide_settings[my_new_option]
and then accessing it with get_option( 'woocommerce_json_api_my_new_option' )
.
Both methods would conceivably support a multi-select as they use maybe_serialize and serialize for data.
When we say The Slug, we mean the slug of the page that is intended to be The API endpoint for requests. When you activate, a page is created for you with a randomized/hashed slug that is more difficult to guess, in this way preventing people from scanning your website for existence of The API. In woocommerce-json-api-core.php you will find a function woocommerce_json_api_exclude_pages($exclude)
which intends to remove The API page from from public listings and nav menus. Some themes may not respect this and list them directly, in which case. It is safe to delete this page that was auto created, and to set The Slug to ANY page in the system you like.
If you simply delete the page, and do not update The Slug, then all pages on your website will serve The API. Not a good idea. This all is facilitated by:
In the file woocommerce-json-api-core.php you will find a function named woocommerce_json_api_template_redirect()
if you look there you will see that we check to see if The Slug is set and IF a page is found that has a slug equal to The Slug, then it calls woocommerce_json_api_shortcode()
, however; if they do not match, it returns out and continues as normal.
HOWEVER If no page is found that matches The Slug, i.e. you deleted it, then this function will evaluate the request to see if it is valid for The API. If so, it will continue, otherwise it will return and release control back to Wordpress.
Once the "shortcode" has been executed ( The shortcode aspect isn't really used, it's just there to mark the page as doing API stuff, we get into:
<?php
function woocommerce_json_api_shortcode() {
$helpers = new JSONAPIHelpers();
$enabled = get_option( $helpers->getPluginPrefix() . '_enabled');
$require_https = get_option( $helpers->getPluginPrefix() . '_require_https' );
if ( $enabled != 'no') {
if ( $require_https == 'yes' && $helpers->isHTTPS() == false ) {
return;
}
$api = new WooCommerce_JSON_API();
$api->setOut('HTTP');
$api->setUser(null);
$api->route($_REQUEST);
}
}
The API satisfies, as best as possible, Dependency Injection. It does not work off $_POST or $_GET or even $_REQUEST, but is provided with a hash with which it decides how to respond and a setting $api->setOut('HTTP');
which tells The API how to respond. The options are:
- HTTP : Will output Headers, JSON, and then die
- JSON : Will return a JSON encoded string
- ARRAY: Will return the modified Params
- OBJECT: Will return the WooCommerce_JSON_API_Result object.
This allows you the broadest flexibility with The API, and the possibility of using it from, or depending on it's functions, another plugin.
WooCommerce is strongly dependent on the see / click / request nature of a website, which makes it difficult to do a bunch of things at once. There is also no consistent facility for updating a Product in the database that abstracts the dirty details of how it is actually saved, which is across tables with various fields.
For this reason, The API was designed to be a springboard into other plugins on top of WooCommerce that required the least about of code duplication from each plugin. It is possible to install The Plugin, completely disable The API from the interface, and simply use it from another plugin to accomplish similar tasks.
In order to do this you should $api->setOut('OBJECT');
and then provide $api->setUser( wp_get_current_user() );
. If we set the user to null, it will attempt to login a user based on the params passed in, i.e. through The Token mechanism.
You can force people to connect through https via The Sitewide Settings, and you should! In this case, attempting to connect without HTTPS will lead to a regular page being returned with no hint of The API's presence. So if you have set this option, and users complain about getting and HTML page, this is probably why.
The API methods are not directly called, as there is some validation going on between calls to ensure that you don't mess everything up. All private API methods are routed via:
In the future, we hope to provide an easy method to hook into the route function and provide new API functionality without having to inherit from the class, or clone and tweak it. This could eventually be facilitated by some form of action hooks.
The API methods are named with get|set_{products,categories,tags}
as well as some additional functions like get_products_by_tags
which allows you to pass the slugs of several tags in and query the products that are attached to those tags.
The internals of The API try as best as possible to do things the WordPress/WooCommerce way, but if functionality is broken (As in the case of tax_query, doesn't work quite right in some versions) or it doesn't make sense to allow other plugins to filter/interfere with something then the functionality is home brewed.
Much of The API is designed so that the back-end processing of requests can be altered easily without disrupting the end users. In the future should new features of Wordpress or WooCommerce become available that necessitate deprecation of what The API is doing, this should not be problematic as The API is a promise of a specific and limited interface to WooCommerce, it is not meant to replace WooCommerce functionality or plugin framework, nor is it meant to be a general purpose Wordpress API. What you can do with the system is necessarily limited to avoid re-inventing the wheel.
For pragmatic purposes, everything to and from The API is considered a collection, even when only 1 result is requested, even when only 1 result would make sense. The result set delivered in The Payload field (both to and from The API) is an Array of whatever is being exchanged.
The API is designed with the idea that what is passed to you, can be altered and passed back without much effort, almost similar to function/procedure calls, or filters. You are returned a result set from get_products
, you can alter any product in the result set, change the data.proc = 'set_products';
and then ship it right back and have your edits committed to the Database, provided you have write permissions, which can be set on the User Settings edit form on the user's profile page.