Make WordPress Plugin compatible with Multisite

Recently, I have been working on a plugin and found out that a plugin working on a single blog doesn’t necessarily means it will work on your multisite environment too. Making your plugin compatible with WordPress multisite is not so hard. But when your plugin creates custom tables or site options on activation of the plugin then you need change some code. I will out line the steps that I followed to make my plugin WordPress multisite compatible.

A little bit of background first. WordPress Multisite was introduced in version 3.0. It enables you to create different site with same WordPress installation.  WordPress.com for example uses this mulitsite feature for end users to create their own site. When a multisite network is created a set of database tables are created which are used globally by all the sites. While certain tables are created with each new site gets registered. This tables are differentiated via a unique blog_id. Whenever a new subsite is created they are given a blog id which an integer value and each table name for this site is appended with the id. For more detail on database structure for multisite can be found over here https://codex.wordpress.org/Database_Description

If we need to create table on single site installation on activation we hook the function to register_activation_hook.

function create_table() {
    global $wpdb;
    $query = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}my_custom_table(
   `id` bigint(20) NOT NULL AUTO_INCREMENT,
   `email` varchar(50) NOT NULL UNIQUE,
   `insert_date` datetime NOT NULL,
   PRIMARY KEY (`id`)
   ) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1";
   require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
   dbDelta($query);
}

The above code will work fine in multisite environment if you activate each site one by one instead of network activate but what happens if you network active the plugin. The code will simply create single table for the main site and will not create tables for its subsite. But we want tables to be created for each of its subsite too. So here we need to tweak the code.

Creating table on plugin activation

My plugin created some table on the activation hook register_activation_hook(). Here is how I changed my code.

 

register_activation_hook( __FILE__, 'activate' );
function activate( $networkwide ) {
      global $wpdb;

      if (function_exists( 'is_multisite' ) && is_multisite() ) {
         //check if it is network activation if so run the activation function for each id
         if( $networkwide ) {
            $old_blog =  $wpdb->blogid;
            //Get all blog ids
            $blogids =  $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );

            foreach ( $blogids as $blog_id ) {
               switch_to_blog($blog_id);
               //Create database table if not exists
               create_table();
            }
            switch_to_blog( $old_blog );
            return;
         }
      }
      //Create database table if not exists
      create_table();
}

function create_table() {
    global $wpdb;
    $query = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}my_custom_table(
   `id` bigint(20) NOT NULL AUTO_INCREMENT,
   `email` varchar(50) NOT NULL UNIQUE,
   `insert_date` datetime NOT NULL,
   PRIMARY KEY (`id`)
   ) DEFAULT CHARSET=utf8 AUTO_INCREMENT=1";
   require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
   dbDelta($query);
}

Here in the function above we checked whether it is multisite installation or not. If it is multisite and is network activated then we need to get all the subsites blog id and loop through each of the id and create table for each single site. The $wpdb->prefix must be used so that it dynamically pulls the database prefix and appends desired blog id for tables of its respective sites.

Deleting tables when site plugin is uninstalled

If a plugin is uninstalled then we need to clean the database table we have created.  So we need to write code to delete table on our uninstall.php file.

if (function_exists( 'is_multisite' ) && is_multisite() ) {
   global $wpdb;

   $old_blog =  $wpdb->blogid;
   //Get all blog ids
   $blogids =  $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );

   foreach ( $blogids as $blog_id ) {
      switch_to_blog($blog_id);
      delete_table();
   }
   switch_to_blog( $old_blog );
} else {
   delete_table();
}

/**
 * Delete custom table and options created by plugin
 *
 * @since 1.0
 * @global WPDB $wpdb
 * @return void
 */
function delete_table(){
   global $wpdb;

   $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}my_custom_table" );
}

There are few cases we need to be careful with. What happens when a new subsite is created or an existing subsite is deleted. We need to either create or destroy the table. Here is how it is done.

add_action( 'wpmu_new_blog', 'create_subscribe_table_mu' );
/**
 * Create subscribe table for new multisite created
 *
 * @return void
 */
function create_subscribe_table_mu( $blog_id, $user_id, $domain, $path, $site_id, $meta ) {
    if ( is_plugin_active_for_network( 'plugin-name/plugin-name.php' ) ) {
        switch_to_blog( $blog_id );
        create_subscribe_table();
        restore_current_blog();
    }
}

Similarly we need to destroy the table when the subsite is deleted. For this we will use wpmu_drop_tables filter.

add_filter( 'wpmu_drop_tables', 'delete_subscribe_table_mu' );
/**
 * Delete subscribe table when multisite blog is deleted
 *
 * @global $wpdb
 * @return void
 */
 public function delete_subscribe_table_mu( $tables ) {
     global $wpdb;
     $tables[] = $wpdb->prefix . 'my_custom_table';
     return $tables;
 }

That’s it your plugin is now ready for multisite.

Make WordPress Plugin compatible with Multisite
Tagged on:
  • wassereimer

    Very good description. 🙂 – All other Websites i found had very bad descriptions and also not all important steps to make a Plugin compatible with WordPress multisite.

  • Rabin

    Glad that you find the article useful 🙂