3

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.

1

2 Answers 2

4

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!

2
  • thanks, this looks like the answer for my purposes (few sites); marked as answer
    – mgtech
    Jun 29, 2018 at 14:29
  • glad to hear it helped @mgtech
    – birgire
    Jun 29, 2018 at 14:33
1

Yes, there is a way to do that. It's a bit complex for here; you have to loop through all sites (after you get a list of sites), do a wp_query for each one, then output results for each wp_query.

Take a look at my "Multisite Post Display" plugin https://wordpress.org/plugins/multisite-post-reader/ . Open source, of course, so you can dig into the code to get hints of how I did it, and adjust for your needs.

Haven't tested performance on a site. Hope it helps.

1
  • Thanks, nice plugin-based solution, but I am specifically looking for something using the REST API. (I'm using vue.js)
    – mgtech
    Jun 26, 2018 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.