web-dev-qa-db-fra.com

Quel est le moyen le plus simple d'implémenter la mise à niveau en cascade de la base de données pour mon plugin?

Supposons la situation suivante:

Je développe un plugin. Le plugin nécessite une table de base de données personnalisée pour stocker certaines informations.

De mois en mois, je publie de nouvelles versions de mon plugin. Différentes versions pourraient avoir leurs propres ensembles de modifications de la structure et des données de la table. Par exemple:

  • la version 1.0.0 a la configuration initiale pour la structure de la table
  • la version 1.1.0 contient des modifications sur un type de colonne et nécessite d'effectuer l'opération de mise à jour sur les données de cette colonne.
  • la version 2.0.0 nécessite la création de deux nouvelles colonnes, le fractionnement des données de l'ancienne colonne en nouvelles colonnes et la suppression de l'ancienne colonne/obsolète

Le problème:

Permet de considérer deux cas d'utilisation:

  1. Un utilisateur a téléchargé la version 1.0.0, ignoré la mise à jour vers la version 1.1.0 et a décidé de mettre à jour le plug-in lors de la publication de la version 2.0.0. Comment organiser le processus de mise à niveau de la base de données, qui gérera la mise à niveau de la version 1.0.0 à la version 2.0.0, y compris les modifications depuis la version 1.1.0?
  2. Un utilisateur a téléchargé la version 2.0.0 et l’a installée sur une instance vierge de WordPress. Comment effectuer la dernière version de l'installation de la base de données, qui inclura toutes les modifications de toutes les versions conformes aux principes DRY?
2
Eugene Manuilov

Ok, pour résoudre ces problèmes, implémentons le processus de mise à niveau en cascade qui gérera les deux cas d'utilisation.

Tout d’abord, implémentons notre hook d’activation de plugin, qui sera notre point d’entrée:

// 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 ) );
}

Avant de commencer à regarder les crochets de mise à niveau, créons une fonction d'assistance qui nous aidera à exécuter un ensemble de requêtes SQL:

function wpse8179_execute_upgrade_queries( array $queries ) {
    global $wpdb;
    foreach ( $queries as $query ) {
        $wpdb->query( $query );
    }
}

Enfin, voyons nos crochets de mise à niveau. Mettre à niveau la base de données de 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;
}

Mettre à niveau la base de données de 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;
}

Mettre à niveau la base de données de 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;
}

Voyons comment notre approche gère les deux cas d'utilisation:

  1. Après la mise à niveau de la version 1.0.0 vers la version 2.0.0, nous allons ignorer le hook wpse8170_upgrade_to_10000 car nous avons déjà la version de la base de données égale à 1.0.0 et passer par les hooks wpse8170_upgrade_to_11000 et wpse8170_upgrade_to_20000 pour mettre à niveau notre base de données vers la dernière version, y compris la version manquante 1.1.0.
  2. Après avoir installé la dernière version du plug-in sur une instance vierge WP, nous allons passer tous les points d'ancrage de mise à niveau et construire notre table de base de données avec toutes les modifications incluses dans les versions 1.0.0 et 1.1.0.
2
Eugene Manuilov