Did you know WordPress Multisite got some powerful new functionality in WordPress 5.1, allowing you to query sites by meta values?
This functionality allows you to query sites in a WordPress Multisite network by meta values and other query-based syntax to get back one or more locations.
Like many who develop WordPress websites, I use the awesome Advanced Custom Fields plugin. What ACF Pro gives you is powerful field functionality that really should be core functionality in WordPress itself.
I need to have an options page that allows me to store unique options against a site and then use WP_Site_Query or get_sites() to query for sites with those specific values. One such use case is categorising sites by country.
Surprisingly, there are not a lot of blog posts or even information on using WP_Site_Query or its related functions in WordPress. In my opinion, it’s some of the most useful functionality introduced in WordPress in a very long time. It opens up a lot of possibilities that required hacking together switch_to_blog calls to achieve prior.
Mirror ACF settings to sitemeta
If you have a custom ACF options page registered using acf_add_options_page, it might look something like this:
acf\_add\_options\_page(array(
'page\_title' => 'Site Settings',
'menu\_title' => 'Site Settings',
'menu\_slug' => 'site-settings',
'capability' => 'edit\_posts',
'redirect' => false
));
This code will create a custom options page that allows us to add in ACF fields as we would to a custom post type or other screens in WordPress.
Behind the scenes, ACF will use the WordPress options API and numerous methods such as update_option to save the values. Sadly, you cannot query WordPress WP_Site_Query to query for sites by options. However, we can mirror our options to sitemeta that can be queried.
Inside of your functions.php file, add in the following code:
add\_action('acf/save\_post', 'mirror\_options\_to\_sitemeta');
function mirror\_options\_to\_sitemeta($post\_id) {
$screen = get\_current\_screen();
if (strpos($screen->id, 'site-settings') == true) {
//print\_r(get\_site\_meta( get\_current\_blog\_id()));
$values = get\_fields( $post\_id );
if ($values) {
foreach ($values as $name => $value) {
update\_site\_meta(get\_current\_blog\_id(), $name, $value);
}
}
}
return true;
}
Using the get_current_screen() method, we determine if the screen is the same as our menu slug from the code above. The acf/save_post action will get fired every time options on our ACF options screen are saved. We do this, so it doesn’t fire for other ACF save calls (acf/save_post is a generic hook called for any ACF save).
Querying WordPress Multisite by meta values
This is the fun part. I have a function called using the WordPress REST API that allows you to query for sites that belong to a specific country.
$site = new WP\_Site\_Query();
sites = $site->query([
'number' => 99999,
'meta\_query' => [
[
'key' => 'country',
'value' => urldecode($request->get\_param('country'))
]
]
]);
I am setting a stupidly high number because there could be hundreds or thousands of sites that come back. The secret sauce of this query is the meta_query which might look familiar if you have ever written meta queries when querying using WP_Post.
A more contrived example if you were to manually query by meta value would be:
$site = new WP\_Site\_Query();
sites = $site->query([
'number' => 99999,
'meta\_query' => [
[
'key' => 'country',
'value' => 'australia'
]
]
]);
This will return an array of WP_Site objects. However, the downside is that the WP_Site object returned might not have helpful information, like the site’s name. This is where you’ll need to use the methods get_blog_details and possibly get_site_meta to get the additional data.