Get Posts or Terms from All Blogs in Multisite Network

How to query all posts or terms in a single loop within WordPress Multisite install. With working pagination.

Query All Network Posts

If you're familiar with WP_Query class, you will be stunningly surprised. Network_Query is its multisite analogue.

$args = array(
	'posts_per_page' => 50,
	'orderby' => 'name',
	'order' => 'ASC'
	// all WP_Query arguments are supported including meta_query, tax_query etc.
);
 
$network_query = new Network_Query( $args ); // just like WP_Query!
 
if( $network_query->have_posts() ) :
 
	while( $network_query->have_posts() ) : $network_query->the_post();
 
		// $network_query->post is a post object - print_r( $network_query->post ) to find out more
		// use build-in functions like network_get_permalink() and network_get_post_thumbnail() to get post URL and thumbnail accordingly
		echo '<h3 id="post-' . $network_query->post->ID . '" class="blog-' . $network_query->post->BLOG_ID . '">
			<a href="' . network_get_permalink( $network_query ) . '">' . $network_query->post->post_title . '</a>
			</h3>';
 
	endwhile;
 
else:
	echo 'No posts in the network for this criteria.';
endif;

Global Search

One search form. Multiple blogs search resuts.
You can search for posts, pages, tags, categories, custom post types and taxonomies.

Multiple blogs search results

Customize

Choose Post Types and Blogs to Index / to Query

post types and global search customization

Unlimited Blogs

Hundreds of blogs in your network? Not a problem — my plugin uses cron jobs to prevent server overload.

Unlimited number of blogs is supported

Support

I'm always here if you need my help.
Please leave your question here or contact me if you need assistance with the plugin.

$args = array(
	'posts_per_page' => 50,
	'orderby' => 'name',
	'order' => 'ASC'
	// all WP_Query arguments are supported including meta_query, tax_query etc.
);
 
$network_query = new Network_Query( $args ); // just like WP_Query!
 
if( $network_query->have_posts() ) :
 
	while( $network_query->have_posts() ) : $network_query->the_post();
 
		// $network_query->post is a post object - print_r( $network_query->post ) to find out more
		// use build-in functions like network_get_permalink() and network_get_post_thumbnail() to get post URL and thumbnail accordingly
		echo '<h3 id="post-' . $network_query->post->ID . '" class="blog-' . $network_query->post->BLOG_ID . '">
			<a href="' . network_get_permalink( $network_query ) . '">' . $network_query->post->post_title . '</a>
			</h3>';
 
	endwhile;
 
else:
	echo 'No posts in the network for this criteria.';
endif;

Global Multisite Search and Custom Post Queries

Just for 29$

Get the plugin or to learn more

So, How to Get All Network Posts in a One Query?

Click the links below to jump to a specific part of the documentation.

A few notes about plugin installation #

This plugin will index all posts in your Multisite Install with custom fields meta_query and terms tax_query. The posts and terms index will be stored in the following database tables.

phpMyAdmin: MySQL database tables with posts and terms indexed from all sites in a network.
MySQL database with the global content inside phpMyAdmin

Now some notes about the plugin installation.

  1. You can download the plugin by clicking the button in the top right part of the screen.
  2. By default this plugin will index only the new posts you publish. If you want to add to the index your old posts, go to Settings > Network Index and then switch to Rebuild Index tab.
Go to the Rebuld Index tab if you want to add your old posts to the network index.
That’s where you can completely rebuild the index

One important thing to know — when you rebuild your global index, it may take a time — how much — depends on the size of your Network. Maybe 10 minutes or maybe a hour.

Example 1. Just Get the Posts like WP_Query #

Let me introduce you the awesome Network_Query class — it works just like WP_Query but for all the network websites and supports all the same parameters + blog_id parameter (I will show this parameter in use in the next example).

Now let’s begin with something simple.

$network_q = new Network_Query('posts_per_page=5'); // display 5 latest global posts
 
if( $network_q->have_posts() ) :
 
	// run the loop for each post
	while( $network_q->have_posts() ) : $network_q->the_post();
 
		// we just get post titles
		// but you can print_r( $network_q->post ) to view all post data
		echo '<h3>' . $network_q->post->post_title . '</h3>';
 
	endwhile;
 
else :
	echo 'No posts found for this criteria';
endif;

Something a little more interesting.

// Network_Query parameters
$args = array(
	'posts_per_page' => 14,
	'orderby' => 'name',
	'order' => 'ASC',
	'meta_query' => array(
		array(
			'key' => 'featured', // we display only posts with custom field 'featured' = 'on'
			'value' => 'on'
		)
	)
);
 
$network_q = new Network_Query( $args );
 
// if there are posts, then print <ul>
if( $network_q->have_posts() ) :
	echo '<ul>';
 
	// run the loop
	while( $network_q->have_posts() ) : $network_q->the_post();
 
		// the get_permalink() function won't work without switch_to_blog()
		// you can use network_get_permalink() instead but it is a little slower
		switch_to_blog( $network_q->post->BLOG_ID );
 
		// you can obtain the post title from $network_q->post object
		echo '<li class="post-' . $network_q->post->ID . ' blog-' . $network_q->post->BLOG_ID . '">
			<a href="' . get_permalink( $network_q->post->ID ) . '">' . $network_q->post->post_title . '</a>
		</li>';
 
		// restore_current_blog() to switch to the previous (!) website
		restore_current_blog();
	endwhile;
 
	echo '</ul>';
endif;
network_reset_postdata(); // add it after the loop if you plan to use Network_Query multiple times on the page

Some comments:

  • In the loop we obtain $network_q->post object — it has all WP_Post object parameters with $network_q->post->BLOG_ID, the ID of the network blog.
  • If you need to do something and the WP_Post object is not enough for your purposes, then use the switch_to_blog() function – it allows you run any WordPress loop functions like get_the_post_thumbnail(), get_permalink() etc. Just do not forget to pass Post ID into these functions.
  • We need restore_current_blog() to cancel the switch_to_blog(), the function switches us to the previous blog — so, use it in a loop.
  • The network_reset_postdata() works similar to wp_reset_postdata().

The next very important feature of the plugin is global search. And you can search not only for posts and pages (post types), you can also perform search for categories and post tags (taxonomies).

Starting with the plugin version 4.4 it is even not required to do anything in the code in order to enable a global search. Just go to Settings > Network Index and then proceed to the Global Settings tab.

If you use the widget option, the global search widget will appear in Appearance > Widgets, or if you would like to replace the default search results, you should also proceed to the Sites admin page and turn it on there as well.
If you use the Widget option, the Global Search Widget will appear in Appearance > Widgets, or if you would like to replace the default search results with the global ones, you should proceed to the Sites admin page and turn this feature on there for each site individually.

With the code you also can:

  • Use WordPress default search engine across the whole network. I mean s= parameter of the Network_Query class.
    $args = array(
    	's' => 'Search query'
    );
     
    $query_search = new Network_Query( $args );
  • Search for posts by their custom fields values — meta_query parameter of the Network_Query class.
    $args = array(
    	'meta_query' => array(
    		'relation' => 'OR',
    		array(
    			'key'     => 'key1', // key1 must contain "wordcamp"
    			'value'   => 'wordcamp',
    			'compare' => 'LIKE'
    		),
    		array(
    			'key'     => 'key2', // OR key2 must contain "wordpress"
    			'value'   => 'wordpress'
    			'compare' => 'LIKE'
    		)
    	)
    );
     
    $query_search = new Network_Query( $args );
  • Search terms by part of their titles and descriptions.
    $network_cats = network_get_terms('category', 'description__like=wordcamp');
    $network_cats = network_get_terms('category', 'name__like=wordpress');

In full global search tutorial I will explain you each of the above examples and show how to replace standart WordPress search form and results page with the global one.

Everything else is like in WP_Query.

Example 3. Get Posts only from the specific blogs in a network. Choose post types to index / to query #

Actually you can:

  • pass post_type or blog_in parameter to the Network_Query() class,
  • configure it in settings (globally for the whole network, or for each blog separately).
post_type
(string|array) It is default WP_Query parameter and I suppose you should already know how it works.
$args = array( 'post_type' => array( 'post', 'page' ) ); // get only posts and pages
blog_id
(int|array) Specify the IDs of the blogs you want to query posts from.
$args = array( 
	'post_type' => 'post', // only posts
	'blog_id' => array( 1, 3 ) // only from blogs 1 and 3
);

Do not know where to get blog ID? Not a problem — my plugin will show blog ID column in your network dashboard Sites > All sites.

Column with Blog ID on the All Sites page
Plugin automatically adds the Blog ID column to the table with the all websites.

You should also know one thing about post_type query parameter — it will work only for indexed post types, for example here I added to the index the attachment post type in the plugin settings:

Add the attachment post type to global network index.

And as I mentioned before, the post types for indexing can be configured individually for each network website. To do this, in your multisite dashboard go to Sites > All sites and click Edit link under the Indexing tab.

Here you can configure what post types to index for each blog individually.
For each blog you can choose what post types to index or completely disable indexing.

Example 4. Query Network Posts with Pagination #

It is always very important for me to implement the pagination for network posts. And this is the simple way how you can do it:

global $wp_query;
 
// get the current page number from $wp_query, I mean the URL parameter, i.e. /page/2
$current_page = (get_query_var('paged')) ? get_query_var('paged') : 1; // you can use $wp_query->query['paged'] as well
 
// similiar to query_posts()
$network_q_posts = network_query_posts( array('posts_per_page' => 3, 'paged' => $current_page) ); 
 
// run the loop
foreach( $network_q_posts as $network_q_post ) :
 
	// we need it to work with get_the_post_thumbnail() and get_permalink()
	switch_to_blog( $network_q_post->BLOG_ID );
 
	echo '<li>' . get_the_post_thumbnail( $network_q_post->ID ) . '<a href="' . get_permalink( $network_q_post->ID ) . '">' . $network_q_post->post_title . '</a></li>';
 
	// switch back
	restore_current_blog();
endforeach;
 
// we should change the global $wp_query value to work correctly with pagination
$wp_query = $GLOBALS['network_query'];
 
// I use the popular WP PageNavi plugin
wp_pagenavi();
 
// reset the $wp_query
wp_reset_query();

If you have problems with this example, you can read full detailed tutorial here.

Example 5. Get categories, post tags globally using get_terms() analogue #

My plugin allows you to get not only post types but taxonomy terms as well. It can be implemented easily with network_get_terms() (click the link to view detailed tutorial).

And here is a simple example:

$network_categories = network_get_terms('category', 'orderby=name&hide_empty=0');
 
if( $network_categories ){
	echo '<select>';
	foreach ( $network_categories as $network_category ){
		echo "<option value='{$network_category->term_id}'>{$network_category->name}</option>";
	}
	echo '</select>';
}
/*
in this example is obvious that
$network_category->name - category name,
$network_category->term_id - category global unique ID across the network
but you can get more info about the category if you look into the object using
print_r( $network_category );
*/

4.7.1 – Sep 18, 2017

  • Added: Feature Images support in VC widget.
  • Fixed: Bug with post type selection in Visual Composer element.

4.7 – Sep 16, 2017

  • Added: Visual Composer element that allows to display posts or pages from any blogs of your Network.
  • Changes in plugin update mechanism.

4.5 – July 25, 2017

  • Added: You can now manage posts from all blogs in a centralized place in Network Dashboard, more info here.

4.4.1 – December 5, 2016

  • Added: network_get_term() function, the get_term() equivalent. The first parameter is $blog_id.

4.4 – December 4, 2016

  • Now you can completely replace the website default search results with the global network search results in the plugin settings without code at all.
  • As an option you can use both default and global search widgets in your website sidebar.

4.3.1 – November 15, 2016

  • Fixed: Warnings when indexing post meta.

4.3 – November 7, 2016

  • Improved: Plugin option pages are optimized for both new and old versions of WordPress.
  • Improved: Plugin performance in /wp-admin/.
  • Fixed: Problem with database tables, when default WordPress database has uncommon columns.
  • Fixed: Problem when saving blog index settings caused by WP-Cirrus plugin.

4.2 – September 23, 2016

  • Bug fixes.
  • Plugin update improvements.

4.1.2

  • Now the blog_id parameter of Network_Query supports array values.

4.1.1

  • Fixed redirect location for manual updates check.

4.1

  • Code optimization and UI improvements.

4.0

  • You can now get terms (categories, post_tags etc) from all blogs in Multisite network in a one loop using network_get_terms() function.
  • Changes in plugin update mechanism.

3.1

  • Changes in plugin update mechanism.
Awesome support from @rudrastyh for his essential plugin "the #wordpress network query for multisite" https://t.co/WqYCXFblIB
Valentin GUENICHON @IFight4TheUsers
I've found best solution to pull all sub-site blog in network into main site. Thanks to @rudrastyh develop #WordPress True Multisite Indexer
Fikri Mastor @FikriMastor
Thank you @rudrastyh for the great plugin, intuitive tutorial and efficient service. I am very happy :) Great job! #wordpress #multisite
Daryl Glass @darylglass
Get Posts or Terms from All Blogs in Multisite Network. Awesome plugin by @rudrastyh! https://t.co/JSzBppO7KE
Mo @M6J33D
  • Hi,

    I’m trying to get the plugin to work with Advanced Custom Fields but seem to only get limited search results.

    For example, where title_text and intro_text are the ACF fields I want to query:

    $args = array(
        's' => get_search_query(), // or $_GET['s']
        'posts_per_page' => -1,
        //'post_type' => array( 'post', 'page', 'acf_field' ),
        //'post_status' => 'publish',
        'meta_query' => array(
            'relation' => 'OR',
            array(
                'key'     => 'title_text', // key1 must contain "wordcamp"
                'value'   => get_search_query(),
                'compare' => 'LIKE'
            ),
            array(
                'key'     => 'intro_text', // OR key2 must contain "wordpress"
                'value'   => get_search_query(),
                'compare' => 'LIKE'
            )
        )
    );

    But this returns no results.

    Thanks,

    Paul

    • Hi Paul,

      Try to use your parameters with regular WP_Query – does it work as expected?

    • I changed it to this:

      $args = array(
          's' => get_search_query(), // or $_GET['s']
          'posts_per_page' => -1,
          'post_type' => array( 'post', 'page', 'acf_field' ),
          'post_status' => 'publish',
          'meta_query' => array(
              'relation' => 'OR',
              array(
                  'key'     => 'title_text', // key1 must contain "wordcamp"
                  'value'   => get_search_query(),
                  'compare' => 'LIKE'
              ),
              array(
                  'key'     => 'intro_text', // OR key2 must contain "wordpress"
                  'value'   => get_search_query(),
                  'compare' => 'LIKE'
              )
          )
      );
      $network_posts = new WP_Query( $args );

      And I received a list of results yes.

      https://gist.github.com/pixel-paul/f9cc2d311f89a17685e7ae4ac3b9f5c9

      • Ok, Paul, thank you for clarifying it. I will test your query on my own server within next 2-3 days.

        • I’ve found the problem. And to confirm this wasn’t returning results, I misread the output.

          The issue is due to the query that is generated is like this:

          OR (wp_posts.post_content LIKE '%media%')))  AND (wp_posts.post_password = '')  AND ( 
            ( wp_postmeta.meta_key = 'default_page_background' AND wp_postmeta.meta_value LIKE '%media%' ) 
            OR 
            ( mt1.meta_key = 'quote_text' AND mt1.meta_value LIKE '%media%' )

          So there is a requirement for the keyword to exist both in the pst_content table AND the meta_value.

          Changing to OR returns results.

        • Thanks for sharing this.

  • Eddy Miranda November 29, 2017 at 18:52

    Im trying to set this plugin up and i have absolutely no idea where to go and make these edits. can you point me in the right direction. im really confused with the instructions.

    • Hello Eddy,

      Please, describe what things would you like to do with the plugin?

      • Eddy Miranda November 29, 2017 at 23:18

        Everything! I want everything this set up so I can run all the blogs Across all the sites with every single function you provide.

      • Ok, to complete the plugin installation and to have the ability to use those functions:

        1. Activate the plugin for the Network (through network dashboard)
        2. Go to Network Admin > Settings > Multisite Indexer, Rebuild Index tab and run the rebuilding process. It will take some amount of time, depending on size of your multisite network.
  • Eddy Miranda November 29, 2017 at 23:45

    Done that. But how do I edit the code for more functionality?

    • Eddy, give me please 1 day until tomorrow and I will write a more detailed installation chapter here, on the plugin’s page :)

      What do you mean under more functionality? Network_Query() class supports all the same parameters like WordPress default WP_Query().

      • Eddy Miranda November 30, 2017 at 16:52

        yes that is what i mean. do you have to do special coding for this?

      • Eddy Miranda November 30, 2017 at 16:54

        because i have absolutely no options to send a blog post to one site or the other.

      • The key feature of Network_Query() is that it is very similar to WP_Query() and supports all the same parameters (https://codex.wordpress.org/Class_Reference/WP_Query#Parameters) + blog_id parameter where you can pass the IDs of the blogs you want to display posts from.

        You can begin with the examples mentioned on this page above, then continue and play with the other parameters.

  • Hi, I’m wondering if this plugin includes post listing for all sites?

  • Hi Misha,
    I’m trying to use the plugin now with the terms.
    I get an empty array for a taxonomy and realize that there’s a weird thing with the indexation of terms. Event after re-indexation.
    For the custom taxonomy I’m interested in, some of the terms have an empty taxonomy through the indexation (see image http://hpics.li/bd12b13).
    Have you an idea to understand what it happens ? Thanks for your help.

    And when I click on a term, I get an error “invalid taxonomy”.

    I did a correct and complete translation of all terms to check if the issue was about same terms across translations. But same error.

    get_terms give me an array and network_get_terms give me an empty array.
    It’s so weird.

    I droped the tables and reinstalled the plugin but same thing : array is empty. Even category is empty while it is not.
    But something new : I don’t have empty taxonomy for terms in the “Recently Indexed Terms” table.

  • Hello Misha,
    is it possible to perform a search Network Query for displaying a post in custom post type?

    • Hello Lorenzo,

      Yes, you can use these parameters for your Network_Query:

      $args = array(
      	'post_type' => 'your post type name here',
      	's' => 'your search query here'
      );
  • Hi Misha, Sorry but I’m back again. I thought I had the exclude working for the tribe events, but apparently not. All posts are still showing from all sites even when the post has a “local” category assigned to it. What am I doing wrong here?

    <?php
    			$today = date("Y-m-d");
    			// Network_Query parameters
    			$args = array(
    			  'post_status'=>'publish',
    			  'post_type'=>array(TribeEvents::POSTTYPE),
    			  'posts_per_page'=> 4,
    			  'meta_key'=>'_EventStartDate',
    			  'orderby'=>'_EventStartDate',
    			  'order'=>'ASC',
    			  'meta_query' => array(
    							array(
    							'key' => '_EventStartDate',
    							'value' => $today,
    							'compare' => '>=',
    								)
    							),
    			  //required in 3.x
    			'eventDisplay'=>'custom',
    			'tax_query' => array(
    					array(
    						'taxonomy' => TribeEvents::TAXONOMY,
    						'field' => 'slug',
    						'terms' => array('local'),
    						'operator' => 'NOT IN'
    					)
    				)
    			);
    $network_q = null;
    $network_q = new Network_Query( $args );
     
    // if there are posts, then print <ul>
    if( $network_q->have_posts() ) :
    	echo '<div class="post-list">';
     
    	// run the loop
    	while( $network_q->have_posts() ) : $network_q->the_post();
     
    		// the get_permalink() function won't work without switch_to_blog()
    		// you can use network_get_permalink() instead but it is a little slower
    		switch_to_blog( $network_q->post->BLOG_ID );
     
    		// you can obtain the post title from $network_q->post object
    		 echo "<div class='post'>";
    // POST CONTAINER
    echo '<h4 class="entry-title"><a href="' . get_permalink( $network_q->post->ID ) . '">' . $network_q->post->post_title . '</a></h4>';
    echo "<div class='meta-date'>";
    echo"<i class=\"fa fa-calendar\" aria-hidden=\"true\"></i> "; 
    echo tribe_get_start_date( $network_q->post->ID, false, 'F j - g:i a' );
    echo" - "; 
    echo tribe_get_end_date( $network_q->post->ID, false, 'F j - g:i a' );
    echo "</div>";
    //	echo get_permalink(), esc_attr(get_the_time()), get_the_date());
    //	echo" | ";
     
    echo "</div>";
     
    		// restore_current_blog() to switch to the previous (!) website
    		restore_current_blog();
    	endwhile;
     
    	echo '</div>';
    endif;
    network_reset_postdata(); // add it after the loop if you plan to use Network_Query multiple times on the page
    ?>
    • Ok so the code works with regular queries, here’s my args for the regular. tribe_events_cat and TribeEvents::TAXONOMY work the same, I just can’t get it to work with the network_q:

      <?php 
      $args = array(
      			  'post_status'=>'publish',
      			  'post_type'=>array(TribeEvents::POSTTYPE),
      			  'posts_per_page'=> 4,
      			  'meta_key'=>'_EventStartDate',
      			  'orderby'=>'_EventStartDate',
      			  'order'=>'ASC',
      			   'tax_query' => array(
      			      array(
      					'taxonomy' => 'tribe_events_cat',
      					'field'    => 'slug',
      					'terms'    => array( 'local' ),
      					'operator' => 'NOT IN'
      					),
      			  ),
      			  'meta_query' => array(
      							array(
      							'key' => '_EventStartDate',
      							'value' => $today,
      							'compare' => '>=',
      								)
      							),
      			  //required in 3.x
      			'eventDisplay'=>'custom',
      			);
      $get_posts = null;
      $get_posts = new WP_Query();
      $get_posts->query($args);
      if($get_posts->have_posts()) : while($get_posts->have_posts()) : $get_posts->the_post(); ?>
       
        <a href="<?php the_permalink(); ?>">
          <?php the_title(); ?>
        </a><br />
       
        <?php if (tribe_get_start_date() !== tribe_get_end_date() ) { ?>
          <?php echo tribe_get_start_date(); ?> - <?php echo tribe_get_end_date(); ?>
        <?php } else { ?>
          <?php echo tribe_get_start_date(); ?>
        <?php } ?>
       
        <?php the_content(); ?>
       
      <?php
        endwhile;
        endif;
        wp_reset_query();
      ?>
  • Hello Misha,
    Is there a way to make yor plugin works with WPML, something like the code below to get only posts in the desired language?

    global $sitepress;
     
            $current_lang = $sitepress->get_current_language(); //save current language
            $sitepress->switch_lang('es');
     
            $args = array(
                'posts_per_page' => 5,
                'orderby' => 'date',
                'order' => 'DESC',
                'suppress_filters' => 0
            );
     
            $query = new Network_Query( $args );
    • Hello,

      Some time ago I tried to make this plugin compatible with WPML. At that moment I come to thoughts that WPML has a very messy logic.

      But my plugin works good enough with another multilingual plugin — Polylang.

Leave your question or feedback

phpjsHTMLCSSSQLCode
Please, enter a comment
Please, enter a name
Incorrect email

Some of the companies that use this plugin

Make Do (Barnsley, UK) Modularte modularte.de (Hanau, Germany) Connective DX (Sydney, Australia) Old River Creative (Woodstock, Virginia, USA)
DotDev (Australia, Brown Hill) Miux (Switzerland, Chur) PurposeWP (United States, Fort Worth) Rovecom (Netherlands, Hoogeveen)

14-Day Money Back Guarantee

Secure Checkout. Instant Download

Recommended

Advanced

$129 one-time payment
  • Lifetime priority support
  • Lifetime updates
  • Unlimited websites

Regular

$29 one-time payment
  • 2 weeks/support
  • 1 year/updates
  • Unlimited websites

View my refund policy here

The plugin is a one-time purchase which includes a license for plugin updates and support. To continue receiving updates & support after your license has expired you may renew at a discounted rate of 50%.