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.