In need of a reliable long term WordPress JSON API, I ended up with this class.

Update: With the recent addition of the REST API in WordPress core, this class is probably less necessary.

<?php
/**
 * Class Simple_Json_Api
 */
class Simple_Json_Api {
	/**
	 * The top level argument for the endpoint.
	 * ex http://example.com/myjson/post/1
	 *
	 * @var string
	 */
	public $endpoint_base = 'myjson';
	/**
	 * Only provide json data for the post_types in this array.
	 *
	 * @var array
	 */
	public $allowed_post_types = array( 'post' );
	/**
	 * Default WP_Query arguments for retrieving posts.
	 * Here you can limit the number of items returned, or change the order
	 * of items returned, etc.
	 *
	 * @var array
	 */
	public $default_query_arguments = array(
		'posts_per_page' => 10,
		'post_status' => array( 'publish' ),
		'orderby' => 'date',
		'order' => 'ASC',
		'ignore_sticky_posts' => true
	);
	/**
	 * Create an array of data for a single post that will be part
	 * of the json response.
	 *
	 * @param $post
	 */
	static public function make_json_data( $post ){
		// featured image urls
		$image_id = get_post_thumbnail_id( $post->ID );
		$image_full  = wp_get_attachment_image_src( $image_id, 'full' );
		$image_thumb = wp_get_attachment_image_src( $image_id, 'thumbnail' );
		$item = array(
			// The global $post object is set, so we can use template tags.
			// Additionally, we have the object, so we could get raw data.
			'title' => get_the_title(),
			'title_raw' => $post->post_title,
			'content' => get_the_content(),
			'content_raw' => $post->post_content,
			'date' => get_the_date(),
			'date_raw' => $post->post_date,
			// meta values
			'my_meta_value' => get_post_meta( $post->ID, 'my_meta_value', TRUE ),
			// all meta values ( not recommended unless you're sure you want to do this )
			//'meta' => get_post_meta( $post->ID ),
			// default image values
			'image_id' => !empty( $image_id ) ? $image_id : false,
			'image_full' => !empty( $image_full[0] ) ? $image_full[0] : false,
			'image_thumb' => !empty( $image_thumb[0] ) ? $image_thumb[0] : false,
			// taxonomy data
			'categories' => get_the_terms( $post->ID, 'category' ),
			'tags' => get_the_terms( $post->ID, 'post_tag' ),
		);
		// OPTIONAL:
		// Depending on your plugin organizational structure, you may want each
		// post_type to be able to control its own json data.  In that case, you 
		// could use a dynamic hook like this to provide that flexibility.
		// return apply_filters( "myjson_api_{$post->post_type}_data", $item, $post );
		return $item;
	}
	/**
	 * Hook the plugin into WordPress
	 */
	static public function register(){
		$plugin = new self();
		add_action( 'init', array( $plugin, 'add_endpoint' ) );
		add_action( 'template_redirect', array( $plugin, 'handle_endpoint' ) );
	}
	/**
	 * Create our json endpoint by adding new rewrite rules to WordPress
	 */
	function add_endpoint(){
		$post_type_tag = $this->endpoint_base . '_type';
		$post_id_tag   = $this->endpoint_base . '_id';
		// Add new rewrite tags to WP for our endpoint's post_type
		// and post_id arguments
		add_rewrite_tag( "%{$post_type_tag}%", '([^&]+)' );
		add_rewrite_tag( "%{$post_id_tag}%", '([0-9]+)' );
		// Add the rules that look for our rewrite tags in the route query.
		// Most specific rule first, then fallback to the general rule
		// specific rule finds a single post
		// http://example.com/myjson/post/1
		add_rewrite_rule(
			$this->endpoint_base . '/([^&]+)/([0-9]+)/?',
			'index.php?'.$post_type_tag.'=$matches[1]&'.$post_id_tag.'=$matches[2]',
			'top' );
		// general rule finds "all" (post_per_page) of a given post_type
		// http://example.com/myjson/post
		add_rewrite_rule(
			$this->endpoint_base . '/([^&]+)/?',
			'index.php?'.$post_type_tag.'=$matches[1]',
			'top' );
	}
	/**
	 * Handle the request of an endpoint
	 */
	function handle_endpoint(){
		global $wp_query;
		// get the query args and sanitize them for confidence
		$type = sanitize_text_field( $wp_query->get( $this->endpoint_base . '_type' ) );
		$id   = intval( $wp_query->get( $this->endpoint_base . '_id' ) );
		
		// only allowed post_types
		if ( ! in_array( $type, $this->allowed_post_types ) ) {
			return;
		}
		// the post_type of the given id must match the requested post_type
		if ( $id && get_post_type( $id ) != $type ) {
			return;
		}
		
		// start with our default query arguments
		$args = $this->default_query_arguments;
		// add the post_type
		$args['post_type'] = array( $type );
		// add the post ID if specified
		if ( $id ) {
			$args['post__in'] = array( $id );
		}
		$query = new WP_Query( $args );
		$data = array();
		// loop through the posts and build our endpoint data arrays
		if ( $query->have_posts() ) {
			while ( $query->have_posts() ) {
				$query->the_post();
				global $post;
				$data[] = self::make_json_data( $post );
			}
			wp_reset_query();
		}
		// data is built. print as json and stop
		wp_send_json( $data ); exit;
	}
}
// huzzah!
Simple_Json_Api::register();

Leave a Reply

Your email address will not be published. Required fields are marked *