I am looking to extend the WordPress API to force it to list posts that are "featured" first and then the rest of the posts.
Basically, I have 2 plugins on my site, one is "NS Featured Posts" (https://wordpress.org/plugins/ns-featured-posts/) and "Post Type Order" (https://wordpress.org/plugins/post-types-order/).
The featured post plugin creates a meta_key of "_is_ns_featured_post" with a value of "Yes" when you flag a post as featured. The "Post Type Order" has a field you can use to order by called "menu_order".
I know how to create a custom endpoint, but I can't figure out how to do the custom order? Can someone help me on that?
John
Luis Abarca answers:
Maybe you can try adding a new endpoint like http://yourdomain.com/wp-json/myapi/featured
add_action('rest_api_init', 'register_myapi_endpoints');
function register_myapi_endpoints()
{
register_rest_route('myapi', '/featured', array(
'methods' => 'GET',
'callback' => 'featured_post_callback'
));
}
function featured_post_callback()
{
// Use WP_Query to get posts
$featured = WP_Query($your_arguments);
$posts = array();
$i = 0;
while ($featured->have_posts()) {
$featured->the_post();
// You can get only the data you need
$posts[$i] = array(
'ID' => $post->ID;
'title' => $post->post_title;
// ....
);
$i++;
}
wp_send_json($posts);
}
Luis Abarca comments:
Hi otherjohn, i put an example to add a custom endpoint
Rempty answers:
Hello otherjohn
By default if you have installed Post Type Order, the api return the posts ordered by the your custom order
/wp-json/wp/v2/posts
If you want to order by a custom meta field (NS Featured Posts), you can add this to your functions.php
function my_allow_meta_query( $valid_vars ) {
$valid_vars = array_merge( $valid_vars, array( 'meta_key', 'meta_value' ) );
return $valid_vars;
}
add_filter( 'rest_query_vars', 'my_allow_meta_query' );
And query like this
/wp-json/wp/v2/posts?filter[meta_key]=_is_ns_featured_post&filter[order]=DESC&filter[orderby]=meta_value
Now you have the list of the featured posts,
For get the other posts without the featured
/wp-json/wp/v2/posts?exclude=id1,id2,id3
(id1,id2,id3 the id of the featured posts)
otherjohn comments:
The suggestion returns the featured at top as expected BUT not in the post type order.
john
Rempty comments:
You can change the url to
/wp-json/wp/v2/posts?filter[meta_key]=_is_ns_featured_post&filter[meta_value]=yes&filter[order]=ASC&filter[orderby]=menu_order
dimadin answers:
This is not tested, but you should get the idea:
function md_featured_and_ordered( $args ) {
$feauterd_args = $not_featured_args = $args;
// First get featured posts
$feauterd_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
),
);
$feauterd_args['orderby'] = 'menu_order';
$feauterd_args['fields'] = 'ids';
$featured = get_post( $featured_args );
// Then we get rest of posts
$not_featured_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
'compare' => 'NOT LIKE',
),
);
$not_featured_args['orderby'] = 'menu_order';
$not_featured_args['fields'] = 'ids';
$not_featured = get_post( $not_featured_args );
$args['post__in'] = array_merge( $featured, $not_featured );
$args['orderby'] = 'post__in'
return $args;
}
add_filter( 'rest_post_query', 'md_featured_and_ordered' );
dimadin comments:
I had some spelling errors there so here are changes:
function md_featured_and_ordered( $args ) {
$featured_args = $not_featured_args = $args;
// First get featured posts
$featured_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
),
);
$featured_args['orderby'] = 'menu_order';
$featured_args['fields'] = 'ids';
$featured = get_post( $featured_args );
// Then we get rest of posts
$not_featured_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
'compare' => 'NOT LIKE',
),
);
$not_featured_args['orderby'] = 'menu_order';
$not_featured_args['fields'] = 'ids';
$not_featured = get_post( $not_featured_args );
$args['post__in'] = array_merge( $featured, $not_featured );
$args['orderby'] = 'post__in';
return $args;
}
add_filter( 'rest_post_query', 'md_featured_and_ordered' );
otherjohn comments:
Im going to test these first. Thanks
dimadin comments:
Oops, my IDE wrote get_post instead of get_posts. Sorry for that.
otherjohn comments:
How do I add this on a Custom Endpoint
dimadin comments:
So do you use /wp-json/wp/v2/posts endpoint or have a custom one? Do you use WP API plugin at all or just support built-in WordPress? If you use custom endpoint, what response do you expect?
dimadin comments:
I added some quick code for custom endpoint, no plugin required. I tested it and it works, but I don't know would you pass some parameters, what number of posts, and what you need in response. Here is workable code:
function md_custom_endpoint() {
register_rest_route( 'custom/v1', '/posts', array(
'callback' => 'md_custom_endpoint_callback',
'methods' => WP_REST_Server::READABLE,
) );
}
add_action( 'rest_api_init', 'md_custom_endpoint' );
function md_custom_endpoint_callback( $request ) {
$featured_args = $not_featured_args = $args = array();
// First get featured posts
$featured_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
),
);
$featured_args['orderby'] = 'menu_order';
$featured_args['fields'] = 'ids';
$featured = get_posts( $featured_args );
// Then we get rest of posts
$not_featured_args['meta_query'] = array(
'relation' => 'OR',
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
'compare' => 'NOT LIKE',
),
array(
'key' => '_is_ns_featured_post',
'compare' => 'NOT EXISTS',
),
);
$not_featured_args['orderby'] = 'menu_order';
$not_featured_args['fields'] = 'ids';
$not_featured = get_posts( $not_featured_args );
$args['post__in'] = array_merge( $featured, $not_featured );
$args['orderby'] = 'post__in';
$posts = get_posts( $args );
$response = rest_ensure_response( $posts );
return $response;
}
otherjohn comments:
Yes, I would pass page, per_page, and categories
dimadin comments:
Here is your code. It works on /wp-json/custom/v1/posts endpoint and accepts arguments you need just like /wp-json/wp/v2/posts.
function md_custom_endpoint() {
register_rest_route( 'custom/v1', '/posts', array(
'callback' => 'md_custom_endpoint_callback',
'methods' => WP_REST_Server::READABLE,
) );
}
add_action( 'rest_api_init', 'md_custom_endpoint' );
function md_custom_endpoint_callback( $request ) {
$featured_args = $not_featured_args = $args = array();
// Get featured posts
$featured_args['meta_query'] = array(
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
),
);
$featured_args['orderby'] = 'menu_order';
$featured_args['fields'] = 'ids';
$featured_args['posts_per_page'] = -1;
$featured = get_posts( $featured_args );
// Get not featured posts
$not_featured_args['meta_query'] = array(
'relation' => 'OR',
array(
'key' => '_is_ns_featured_post',
'value' => 'yes',
'compare' => 'NOT LIKE',
),
array(
'key' => '_is_ns_featured_post',
'compare' => 'NOT EXISTS',
),
);
$not_featured_args['orderby'] = 'menu_order';
$not_featured_args['fields'] = 'ids';
$not_featured_args['posts_per_page'] = -1;
$not_featured = get_posts( $not_featured_args );
// Get all posts
$args['paged'] = $request['page'];
$args['posts_per_page'] = $request['per_page'];
$taxonomies = wp_list_filter( get_object_taxonomies( 'post', 'objects' ), array( 'show_in_rest' => true ) );
foreach ( $taxonomies as $taxonomy ) {
$base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name;
if ( ! empty( $request[ $base ] ) ) {
$args['tax_query'][] = array(
'taxonomy' => $taxonomy->name,
'field' => 'term_id',
'terms' => $request[ $base ],
'include_children' => false,
);
}
}
$args['post__in'] = array_merge( $featured, $not_featured );
$args['orderby'] = 'post__in';
$posts = get_posts( $args );
$response = rest_ensure_response( $posts );
return $response;
}
webGP answers:
Hello otherjon!
As I understand you try to show all posts ordered by two parameters - fetaure (first posts) and menu_order. Try to add the code below into your functions.php
add_action('rest_api_init', 'register_myapi_endpoints');
function register_myapi_endpoints() {
register_rest_route('custom-api', '/posts-ordered', array(
'methods' => 'GET',
'callback' => 'posts_ordered'
));
}
function posts_ordered() {
$args = array (
'post_type' => 'post',
'post_status'=> 'publish',
'posts_per_page'=> -1,
'orderby' => array( 'meta_value' => 'DESC', 'menu_order' => 'ASC' ),
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_is_ns_featured_post',
'compare' => 'EXISTS'
),
),
);
$posts = get_posts($args);
wp_send_json($posts);
}
add_action( 'save_post', 'override_feature_meta', 999 );
function override_feature_meta($post_id) {
$featured_value = '';
if ( isset( $_POST['nsfp_settings']['make_this_featured'] ) && 'yes' == $_POST['nsfp_settings']['make_this_featured'] ) {
$featured_value = 'yes';
}
if ( 'yes' !== $featured_value ) {
update_post_meta( $post_id, '_is_ns_featured_post', 'no' );
}
return $post_id;
}
Your posts will be available at http://yourdomain.com/wp-json/custom-api/posts-ordered. Of course if you need use category or page params add standard args to $args array.
<strong>IMPORTANT!</strong>
"NS Featured Posts" plugin doesn't set meta for non-featured posts, which brokes sorting, so you have to update all posts in admin. I've added action which save meta with 'no' value.