WordPress Multi-Site: Switching Blogs

WordPressEarlier today, I was working on a new plug-in for one of my WordPress Multi-Site networks. I wanted to pull a list of top-level pages from each site/blog within the network, but I didn’t want the overhead of using the switch_to_blog() function. All I really wanted to do was to query the appropriate tables within the database, but, with WordPress Multi-Site’s implementation of table prefixes, there was no reliable way to attempt to ascertain the correct table name.

It took me a while to figure out how to do this, but I finally did figure it out.

It should be noted that this method does not call or apply any of the filters associated with the blog to which you’re switching; it only modifies the $wpdb object so that it queries a different set of tables in the database. It should also be noted that this has only been tested on installations with a single WordPress database. If you have been brave enough to split your WordPress Multi-Site installation across multiple databases, I have no idea if this will work or not.

All that said, here is the method I used:

function umw_get_blog_posts( $blogid=1, $type='post', $parent=0 ) {
	global $wpdb;
	$wpdbobj = clone $wpdb;
	$wpdb->blogid = $blogid;
	$wpdb->set_prefix( $wpdb->base_prefix );
	$query = $wpdb->prepare( "SELECT ID, post_title, post_name
		FROM $wpdb->posts
		WHERE post_status='publish'
		AND post_type='$type'
		AND post_parent=$parent" );
	$wpdb = clone $wpdbobj;
	return $wpdb->get_results( $query, ARRAY_A );

The keys here are the $wpdb object, the base_prefix and blogid properties of that object and the set_prefix() method of that object.

The $wpdb object is the root object that WordPress uses to perform all actions in the database. Updates, selects, deletes, etc. are all performed through the $wpdb object. In order to use it, you’ll need to grab it from the global scope, which you do so by placing global $wpdb; at the start of your code block or function definition.

Because you’re actually altering the $wpdb object, you should actually clone it first, then restore it when you’re done with your actions. That way, when you return to the rest of the actions that need to be performed on your site, it won’t be querying the wrong tables.

The base_prefix property is the table prefix that’s used for global tables within the network. Then, each site set up within the network has it’s own prefix appended to the end of the base prefix (although, in the case of WordPress Multi-Site, the first site uses the base prefix, but in WordPressMU, the first site followed the same standard as additional sites).

For instance, if your base prefix is “wp_” (as is the default setting for WordPress installations) and you want to query tables related to the site with an ID of 4, the prefix for those tables would be “wp_4_”. You could, of course, attempt to build the prefix yourself through your script, but you’d then have to account for the fact that WordPressMU and WordPress Multi-Site handled the root site differently (there are some other pitfalls to trying to do it yourself, but that’s the easiest one to point out at the moment).

That’s where the blogid property and set_prefix() method come into play. If you set the blogid property to the ID of the site from which you want to pull information, then you use the set_prefix() method to set the prefix that’s used by the $wpdb object when querying the tables. With those three items, WordPress automatically knows which tables to query when you run a query against the database.

Therefore, the minimum code you really need in order to query a site other than the one currently being viewed would be:

global $wpdb;
/* Set $myBlogId to the ID of the site you want to query */
$wpdb->blogid = $myBlogId;
$wpdb->set_prefix( $wpdb->base_prefix );

Note about WordPress queries: When querying the database (whether you’re trying to access a different blog or not), make sure you use the table properties of the $wpdb object, rather than trying to hardcode the table names into your query. For instance, instead of trying to query wp_posts, you should query $wpdb->posts. You should also use the $wpdb->prepare() method to evaluate and protect the query against SQL injection.

Note about the $wpdb->base_prefix property: At first, I tried using the global $table_prefix variable (which is initially set in your wp-config.php file), but I quickly discovered that that variable is modified to be specific to the site you’re currently visiting. For instance, if you’re currently viewing the site with an ID of 4 and your base prefix is set to “wp_”, the $table_prefix variable will be set to “wp_4_” instead of “wp_”. That’s why it’s important to use the $wpdb->base_prefix property instead of the $table_prefix variable.

Note about terminology used in this article: For the purposes of this article, I have attempted to use the terminology associated with WordPress Multi-Site, which differs from the terminology originally used with WordPressMU (and, therefore, since most of the WordPress Multi-Site functions and classes were used from WPMU with little modification, the terminology actually used in variables, properties and methods). Some examples include the use of the word “site”. In WPMU, a “site” was a collection of “blogs”. In WordPress Multi-Site, a “network” is a collection of “sites”. Therefore, from WPMU to WordPress Multi-Site, the translation is as follows: “site” becomes “network” and “blog” becomes “site”.

That’s why, for instance, when we are looking for the ID of the current site being viewed, the variable is named “blogid”.

4 Responses

  • […] I took the liberty of using some code I posted here on HTMLCenter a few weeks ago as a starting point, then writing up some functions to pull featured images from other blogs in the […]

  • So, now that you’ve returned the results in an array, what code would you suggest to take it apart (to access, say, post_title)?

    BTW, this approach is wonderful. I’ve been searching and searching for a solution to accessing the posts of a network’s sites w/o using the plugin Sitewide Tags (which is the default answer for everyone on the WP forums), the resource-intense switch_to_blog() or a custom select query that required hard-coding the table names, etc.

  • Scratch that – the foreach loop is in check.

    …Now I’m trying to figure out how to pull the permalink for each post.

  • Bufthund

    Thank you so much for this