WordPress is incredibly powerful, and every so often, a new feature gets added that goes under the radar that can dramatically change how you build sites. In WordPress 5.1, an addition of a new WP_Site_Query class was created. It allows you to query your network of sites but goes beyond just getting IDs and making you loop over them to use with switch_to_blog($id)
Here is another super-specific use case I had with WordPress Multisite recently.
I have a network comprised of thousands of sites. Each site has an options page that uses Advanced Custom Fields Pro. The information is things like the site’s name, country, etc.
I needed to implement a search that allowed me to query one or more of these meta fields. In my situation, each site belongs to a continent, country, state/region, local government area or city/town. I needed to retrieve a list of these sites based on a search keyword.
Here is how I did it.
First, I created a custom REST endpoint.
add_action( 'rest_api_init', function () { register_rest_route( 'utilities/v1', '/sites/search/(?P<search>([a-zA-Z]|%20)+)', array( 'methods' => 'GET', 'callback' => 'rest_search_sites', 'permission_callback' => '__return_true' ) ); });
A pretty standard REST API route. Take note of how we create our parameter regex, though. We want to allow letters but also allow spaces as well. If someone types “United States of America,” it can see the whole term.
And here is the callback function for our route.
function rest_search_sites( WP_REST_Request $request ) { $value = $request->get_param( 'search' ); $site = new WP_Site_Query(); $sites_found = $site->query([ 'number' => 9999, 'meta_query' => [ 'relation' => 'OR', [ 'key' => 'site_name', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'country', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'continent', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'state', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'region', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'local_government_area', 'value' => $value, 'compare' => 'LIKE' ], [ 'key' => 'city-town', 'value' => $value, 'compare' => 'LIKE' ], ] ]); $result = new WP_REST_Response([]); if ( $sites_found ) { $sites = []; foreach ($sites_found as $s) { switch_to_blog($s->blog_id); $details = get_blog_details(); $meta = get_site_meta($s->blog_id); $obj = new stdClass; $obj->blog_id = $s->blog_id; $obj->blogname = $details->blogname; $obj->siteurl = $details->siteurl; $obj->home = $details->home; $obj->meta = site_meta_object($meta); restore_current_blog(); $sites[] = $obj; } $result = new WP_REST_Response( $sites ); } $result->set_headers(array('Cache-Control' => 'max-age=3600')); return $result; }
You are probably thinking I am crazy, but it works. I query the network using an OR meta query, meaning that only one of these has to match to return a result, and I do it for a few meta fields I have (created using ACF Pro).
The bit at the end is where I loop over the IDs. I get information about the site and construct an object of metadata and other details I might need on the front end.
It’s remarkably simple but powerful. This opens up the possibility of creating sites that are categorised by meta values and those values being searchable.