Is it possible / how can I get all posts from all sites on a multisite installation using the REST API? On the front page of the primary site, I want to display recent posts from all the sites; e.g., mysite.com shows recent posts from mysite.com/site1, myite.com/site2, etc., merged together (possibly with some filtering). I know how to do this with PHP; I want to see if I can do it using the REST API.
Josh Pollock @ torquemag has proposed this solution:
https://torquemag.io/2016/07/combine-wordpress-sites-rest-api/
Josh’s example envisions combining posts from 2 completely different sites, and so I’m wondering if there is another way to do this within a single multisite install. I haven’t done much with custom endpoints til now and wondering if a custom endpoint could accomplish this as well.
Also curious about any potential performance issues involved.
Here’s a REST API recipe for latest post per site on a multi-site, for relatively few sites:
- Use
get_sites()
,
- loop over sites and switch to each site,
- query latest post on each site,
- collect data,
- order data by utime,
- serve data
Example REST Routes
https://example.com/wp-json/wpse/v1/latest-post-per-site
https://example.com/wp-json/wpse/v1/latest-post-per-site?number=10
https://example.com/wp-json/wpse/v1/latest-post-per-site?debug
Example Plugin
Here’s a demo plugin implementing the above recipe (needs testing):
<?php
/**
* Plugin Name: WPSE-307040: Rest API - Latest Post Per Site
* Description: Rest API - latest post per site (multisite).
* Plugin URI: https://wordpress.stackexchange.com/a/307051/26350
* Author: birgire
* Version: 1.0.0
*/
namespace WPSE\RestAPI\LatestPostPerSite;
/**
* Register Rest Route
*/
add_action( 'rest_api_init', function() {
register_rest_route(
'wpse/v1',
'/latest-post-per-site/',
[
'methods' => 'GET',
'callback' => __NAMESPACE__.'\rest_results'
]
);
} );
/**
* Rest Results
*/
function rest_results( $request ) {
$parameters = $request->get_query_params();
// Default 10 items
$number = isset( $parameters['number'] ) ? (int) $parameters['number'] : 10;
// Max 30 items
if( $number > 30 ) {
$number = 30;
}
$items = [];
$i = 0;
$blogs = get_sites( [
'orderby' => 'last_updated',
'order' => 'DESC',
'number' => (int) $number
] );
foreach ( $blogs as $blog ) {
switch_to_blog( $blog->blog_id );
// Site info
$details = get_blog_details( $blog->blog_id );
$items[$i]['sitename'] = esc_html( $details->blogname );
$items[$i]['siteid'] = (int) $blog->blog_id;
$items[$i]['homeurl'] = esc_url( $details->home );
// Latest post
$args = [
'orderby' => 'post_date',
'order' => 'DESC',
'posts_per_page' => 1,
'post_type' => 'post',
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
];
$query = new \WP_Query( $args );
if( $query->have_posts() ) {
while( $query->have_posts() ) {
$query->the_post();
// Title
$items[$i]['title'] = esc_html( get_the_title( get_the_ID() ) );
// Date
$items[$i]['date'] = esc_html( get_the_date( 'Y-m-d H:i:s', get_the_ID() ) );
// Unix time
$items[$i]['utime'] = esc_html( get_the_date( 'U', get_the_ID() ) );
// Excerpt
$items[$i]['excerpt'] = esc_html( wp_trim_words( strip_shortcodes( get_the_content() ), 50 ) );
// Permalink
$items[$i]['url'] = esc_url( get_permalink( get_the_ID() ) );
}
wp_reset_postdata();
}
$i++;
restore_current_blog();
// Sort by utime.
$items = wp_list_sort( $items, 'utime', 'DESC' );
$data = [
'success' => true,
'count' => count( $items ),
'items' => $items,
];
// Debug for super admins.
if( isset( $parameters['debug'] ) && current_user_can( 'manage_networks' ) ) {
$data['qrs'] = get_num_queries();
$data['sec'] = timer_stop(0);
}
return $data;
}
For larger number of sites, I would instead consider hooking into the create/update/delete for post/meta/taxonomy/term on each site, to sync and collect site wide data to a dedicated data site in the multi-site network.
Hope it helps!