boxybird / boxybird-wp-query-endpoint
Query anything you want from the WordPress database using a single REST API endpoint.
README
Query anything you want from the WordPress database using a single REST API endpoint.
Simply put, this plugin allows to pass GET params from a url into the WP_Query
class as $args
WP_Query
Reference: https://developer.wordpress.org/reference/classes/wp_query/
Installation
Clone or Download plugin and run composer install
before activating in WordPress Admin.
Usage Examples
PHP
Normally in a WordPress theme or plugin you would create an array of $args and pass them into the WP_Query($args)
constructor. Like this:
<?php $args = [ 'post_type' => 'post', 'orderby' => 'title', 'posts_per_page' => 12, 'category__in' => [31, 12, 4], //... any other WP_Query args you want. ]; $query = new WP_Query($args); // loop through results in PHP file.
Using this plugin, you can pass those same args from your front-end Javascript:
Vanilla JS
const url = 'https://your-site.com/wp-json/boxybird/wp-query/v1/args?post_type=post&orderby=title&posts_per_page=12&category__in[]=31&category__in[]=12&category__in[]=4' fetch(url) .then(res => res.json()) .then(data => { console.log(data) })
jQuery
const params = jQuery.param({ post_type: 'post', orderby: 'title', posts_per_page: 12, category__in: [31, 12, 4], //... any other WP_Query args you want. }); jQuery.get(`https:/your-site.com/wp-json/boxybird/wp-query/v1/args?${params}`).done((data) => { console.log(data); });
Hooks
The examples below will use a hypothetical site with a 'movie' post_type.
Formatting the JSON response
Out of the box, WP_Query
will return the raw rows from the wp_posts
table. Like this:
{ "success": true, "data": [ { "ID": 569, "post_author": "1", "post_date": "2020-11-23 00:17:16", "post_date_gmt": "2020-11-23 00:17:16", "post_content": "An eclectic foursome of aspiring teenage witches get more than they bargained for as they lean into their newfound powers.", "post_title": "The Craft: Legacy", "post_excerpt": "", "post_status": "publish", "comment_status": "closed", "ping_status": "closed", "post_password": "", "post_name": "the-craft-legacy", "to_ping": "", "pinged": "", "post_modified": "2020-11-23 00:17:16", "post_modified_gmt": "2020-11-23 00:17:16", "post_content_filtered": "", "post_parent": 0, "guid": "https://wp-query.andrewrhyand.com/movies/the-craft-legacy", "menu_order": 0, "post_type": "movie", "post_mime_type": "", "comment_count": "0", "filter": "raw" }, { "ID": 567, "post_author": "1", "post_date": "2020-11-23 00:17:16", "...and so on" } ] }
The above may be useful in some situations, but more often than not you'll likely want to format the JSON response. This is the filter to do it:
add_filter('boxybird/query/format-response', function (WP_Query $query) { // do something with $query and return. });
Here's an example of how you could use the filter:
add_filter('boxybird/query/format-response', function (WP_Query $query) { // Assign queried 'post_type' $post_type = strtolower($query->query_vars['post_type']); // If it's a 'movie' post_type, format like this: if ($post_type === 'movie') { return array_map(function ($movie) { return [ 'id' => $movie->ID, 'title' => get_the_title($movie->ID), 'description' => get_the_content(null, false, $movie->ID), 'link' => get_the_permalink($movie->ID), 'genres' => array_map(function ($term) { return $term->name; }, get_the_terms($movie->ID, 'genre')), 'details' => array_map(function ($detail) { return $detail[0]; }, get_post_meta($movie->ID)), 'description' => [ 'short' => wp_trim_words(get_the_content(null, false, $movie->ID), 10), 'long' => wp_trim_words(get_the_content(null, false, $movie->ID), 75), ], 'images' => [ 'full' => get_the_post_thumbnail_url($movie->ID, 'full'), 'medium' => get_the_post_thumbnail_url($movie->ID, 'medium'), 'thumbnail' => get_the_post_thumbnail_url($movie->ID, 'thumbnail'), ], ]; }, $query->posts); } // If it's a 'post' post_type, format like this: if ($post_type === 'post') { return array_map(function ($post) { return [ 'id' => $post->ID, 'title' => get_the_title($post->ID), 'content' => get_the_content(null, false, $post->ID), 'link' => get_the_permalink($post->ID), ]; }, $query->posts); } // If it's any other post_type, format like this: return array_map(function ($post) { return [ 'id' => $post->ID, 'title' => get_the_title($post->ID), ]; }, $query->posts); });
Focusing on the 'movie' post_type above, this would be the custom formatted response:
{ "success": true, "data": [ { "id": 553, "title": "Dark Phoenix", "description": "The X-Men face their most formidable and powerful foe when one of their own, Jean Grey, starts to spiral out of control. During a rescue mission in outer space, Jean is nearly killed when she's hit by a mysterious cosmic force. Once she returns home, this force not only makes her infinitely more powerful, but far more unstable. The X-Men must now band together to save her soul and battle aliens that want to use Grey's new abilities to rule the galaxy.", "link": "https://wp-query.andrewrhyand.com/movies/dark-phoenix", "genres": [ "Drama", "Horror", "Thriller" ], "details": { "budget": "200000000", "status": "Released", "tmdb_id": "320288", "imdb_id": "tt6565702", "revenue": "252442974", "runtime": "114", "tagline": "X-Men Dark Phoenix", "homepage": "http://darkphoenix.com", "popularity": "122.285", "vote_count": "4063", "vote_average": "61%", "release_date": "Jun 05, 2019", "_thumbnail_id": "554" }, "short_description": { "short": "The X-Men face their most formidable and powerful foe when…", "long": "The X-Men face their most formidable and powerful foe when one of their own, Jean Grey, starts to spiral out of control. During a rescue mission in outer space, Jean is nearly killed when she's hit by a mysterious cosmic force. Once she returns home, this force not only makes her infinitely more powerful, but far more unstable. The X-Men must now band together to save her soul and battle aliens that want to use…" }, "images": { "full": "https://wp-query.andrewrhyand.com/wp-content/uploads/2020/11/cCTJPelKGLhALq3r51A9uMonxKj.jpg", "medium": "https://wp-query.andrewrhyand.com/wp-content/uploads/2020/11/cCTJPelKGLhALq3r51A9uMonxKj-200x300.jpg", "thumbnail": "https://wp-query.andrewrhyand.com/wp-content/uploads/2020/11/cCTJPelKGLhALq3r51A9uMonxKj-150x150.jpg" } }, { "id": 551, "title": "Enemy Lines", "description": "In the frozen, war torn landscape of occupied Poland during World War II, a crack team of allied commandos are sent on a deadly mission behind enemy lines to extract a rocket scientist from the hands of the Nazis.", "...and so on" } ] }
Default/Overriding WP_Query Args
Default Args
If you would like to add default WP_Query $args
BEFORE the request params are applied, this is the filter to do it.
// Example add_filter('boxybird/query/default-args', function ($args) { $args['posts_per_page'] = 12; return $args; });
Note: The above are defaults only. Meaning, if the incoming request specifies
posts_per_page
, it will override theboxybird/query/default-args
filter defaults.
Override Args
If you would like to modify/remove incoming request params BEFORE running the WP_Query
, this is the filter to do it.
// Example. add_filter('boxybird/query/override-args', function ($args) { // Don't allow more than 20 'posts_per_page'. if (isset($args['posts_per_page']) && $args['posts_per_page'] > 20) { $args['posts_per_page'] = 20; } // Always override 'post_status' $args['post_status'] = 'publish'; return $args; });
Note: The above filter can be thought of as a security layer. If you never want an
$arg
to be passed toWP_Query
, do it here!
Permissions Callback
If you would like to protected who has access to the /wp-json/boxybird/wp-query/v1/args
endpoint, this is the filter.
// Basic Example. add_filter('boxybird/query/permission', function () { // Only logged in users have access. return is_user_logged_in(); }); // Example taken from the WordPress docs. add_filter('boxybird/query/permission', function () { // Restrict endpoint to only users who have the edit_posts capability. if (!current_user_can('edit_posts')) { return new WP_Error('rest_forbidden', esc_html__('OMG you can not view private data.', 'my-text-domain'), ['status' => 401]); } // This is a black-listing approach. You could alternatively do this via white-listing, by returning false here and changing the permissions check. return true; });