为我的插件实现级联数据库升级最简单的方法是什么?

时间:2013-02-06 作者:Eugene Manuilov

Lets assume following situation:

我开发了一个插件。插件需要自定义数据库表来存储某种信息。

每月我都会发布插件的新版本。不同版本的表结构和表数据可能有自己的更改集。例如:

1.0.0版对表结构进行了初始设置,1.1.0版更改为一种列类型,需要对该列的数据执行更新操作

The problem:

让我们考虑两个用例:

一位用户下载了1.0.0版,跳过了1.1.0版的更新,并决定在2.0.0版发布时更新插件。如何组织数据库升级过程,该过程将处理从版本1.0.0到版本2.0.0的升级,包括从版本1.1.0的更改

1 个回复
最合适的回答,由SO网友:Eugene Manuilov 整理而成

好的,为了解决这些问题,让我们实现级联升级过程,它将处理这两个用例。

首先,让我们实现插件激活挂钩,这将是我们的切入点:

// define current plugin version
define( \'WPSE8170_PLUGIN_VERSION\', \'2.0.0\' );
// define our database table name
define( \'WPSE8170_DB_TABLE\', $GLOBALS[\'wpdb\']->prefix . \'wpse8170_test_table\' );

add_action( \'init\', \'wpse8170_plugin_upgrade\' ); // check database on init action, to be confident that our plugin database is up-to-date
register_activation_hook  ( __FILE__, \'wpse8170_plugin_upgrade\' );
function wpse8170_plugin_upgrade() {
    $filter = \'wpse8170_upgrade_db\';
    $option = \'wpse8170_db_version\';

    // get current database version
    $db_version = get_option( $option );

    // if database version is not exists, lets create new and set it to \'0.0.0\'
    if ( $db_version === false ) {
        $db_version = \'0.0.0\';
        add_option( $option, $db_version, \'\', \'yes\' );
    }

    // check database version, if it equals to current plugin version, then no upgrades are required
    if ( version_compare( $db_version, WPSE8170_PLUGIN_VERSION, \'=\' ) ) {
        return;
    }

    // define our upgrade hooks, which will be called to upgrade database to a certain version
    add_filter( $filter, \'wpse8170_upgrade_to_10000\' ); // upgrade db to version 1.0
    add_filter( $filter, \'wpse8170_upgrade_to_11000\' ); // upgrade db to version 1.1
    add_filter( $filter, \'wpse8170_upgrade_to_20000\' ); // upgrade db to version 2.0

    // apply our upgrade filter and update database version 
    update_option( $option, apply_filters( $filter, $db_version ) );
}
在开始研究升级挂钩之前,让我们创建一个助手函数,它将帮助我们执行一组sql查询:

function wpse8179_execute_upgrade_queries( array $queries ) {
    global $wpdb;
    foreach ( $queries as $query ) {
        $wpdb->query( $query );
    }
}
最后,让我们看看我们的升级挂钩。将数据库从0.0.0升级到1.0.0:

function wpse8170_upgrade_to_10000( $current_version ) {
    // define version of current upgrade hook
    $this_version = \'1.0.0\';

    // if the version of current upgrade hook is less or equals to current database version, return it without any changes
    if ( version_compare( $current_version, $this_version, \'>=\' ) ) {
        return $current_version;
    }

    // execute all required queries to make database corresponding to current upgrade version
    wpse8179_execute_upgrade_queries( array(
        sprintf( "CREATE TABLE IF NOT EXISTS `%s` (`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `type` VARCHAR(15) NOT NULL, `data` TEXT NOT NULL, PRIMARY KEY (`id`) ) ENGINE = MyISAM", WPSE8170_DB_TABLE ),
        // above queries could be merged into one, but added as an example
        sprintf( "ALTER TABLE `%s` CHARACTER SET = utf8, COLLATE = utf8_general_ci;", WPSE8170_DB_TABLE ),
        sprintf( "ALTER TABLE `%s` CHANGE COLUMN `data` `data` TEXT CHARACTER SET \'utf8\' COLLATE \'utf8_general_ci\' NOT NULL;", WPSE8170_DB_TABLE ),
        sprintf( "ALTER TABLE `%s` ADD INDEX `gchart_idx_type` (`type` ASC)", WPSE8170_DB_TABLE ),
    ) );

    // return current upgrade version, which is equals to 1.0.0
    return $this_version;
}
将数据库从1.0.0升级到1.1.0:

function wpse8170_upgrade_to_11000( $current_version ) {
    // define version of current upgrade hook
    $this_version = \'1.1.0\';

    // if the version of current upgrade hook is less or equals to current database version, return it without any changes
    if ( version_compare( $current_version, $this_version, \'>=\' ) ) {
        return $current_version;
    }

    // execute all required queries to make database corresponding to current upgrade version
    wpse8179_execute_upgrade_queries( array(
        // update table column type
        sprintf( "ALTER TABLE `%s` CHANGE COLUMN `data` `data` MEDIUMTEXT NOT NULL", WPSE8170_DB_TABLE ),
        // update table data
        sprintf( "UPDATE `%s` SET ... WHERE ...", WPSE8170_DB_TABLE ),
    ) );

    // return current upgrade version, which is equals to 1.1.0
    return $this_version;
}
将数据库从1.1.0升级到2.0.0:

function wpse8170_upgrade_to_20000( $current_version ) {
    // define version of current upgrade hook
    $this_version = \'2.0.0\';

    // if the version of current upgrade hook is less or equals to current database version, return it without any changes
    if ( version_compare( $current_version, $this_version, \'>=\' ) ) {
        return $current_version;
    }

    // execute all required queries to make database corresponding to current upgrade version
    wpse8179_execute_upgrade_queries( array(
        // update table by adding two new columns
        sprintf( "ALTER TABLE `%s` ...", WPSE8170_DB_TABLE ),
        // update table data by splitting data from old column into two new
        sprintf( "UPDATE `%s` SET ... WHERE ...", WPSE8170_DB_TABLE ),
        // delete deprecated column from the table
        sprintf( "ALTER TABLE `%s` ...", WPSE8170_DB_TABLE ),
    ) );

    // return current upgrade version, which is equals to 2.0.0
    return $this_version;
}
让我们看看我们的方法如何处理这两个用例:

从1.0.0版升级到2.0.0版后,我们将跳过挂钩wpse8170_upgrade_to_10000 因为我们已经有数据库版本等于1.0.0 并穿过挂钩wpse8170_upgrade_to_11000wpse8170_upgrade_to_20000 将数据库升级到最新版本,包括缺失的1.1.0版本在空白WP实例上安装最新版本的插件后,我们将通过所有升级挂钩,并使用版本1.0.0和1.1.0中包含的所有更改构建数据库表

结束