Hoy traigo un codekata o howto para el desarrollo de plugins para WordPress, con una funcionalidad que me ha entretenido bastante el conseguir que funcione. Se trata de una forma de actualizar las tablas de la base de datos, a medida que vamos actualizando un plugin, y vamos modificando también la estructura o contenidos de la base de datos. Es decir, es un pequeño howto o tutorial para mantener versiones de la estructura y contenido de la base de datos, para un plugin de WordPress.
Vayamos al grano..
Ideas principales, un poco de teoría
Los sistemas de versionado para bases de datos, funcionan manteniendo información sobre qué versión de la base de datos está actualmente funcionando. En paralelo, mediante programación, se consulta la versión actual, y se aplican los cambios programados.
Ya está, esto es todo lo que hay se saber para hacerlo funcionar, tendremos:
- En la BD, tendremos almacenada la versión actual de la BD, como si de otra variable de WordPress se tratara.
- En el código fuente, consultamos la versión actual, y si es menor que las versión en código fuente, vamos aplicando las actualizaciones programadas.
Show me the code, el código fuente
Inicialmente tenemos que guardar en WordPress una opción para saber qué versión de la base de datos tenemos actualmente. Con cada nueva versión de la BD que se aplique, hay que actualizar esta opción. Para crear una opción en WP podemos hacer algo parecido a lo siguiente:
register_setting('jnj_options_group', 'jnj_db_version');
add_option('jnj_db_version', 1);
Cada vez que se aplique una nueva versión, se puede actualizar esta variable haciendo:
update_option('jnj_db_version', $current_version);
Lo siguiente es tener una versión inicial de la BD, que puede ser una simple tabla con 2 columnas. Se podría hacer algo como lo siguiente:
global $wpdb;
// Main table..
$sql = 'CREATE TABLE jnj_test ('
.'time DATETIME NOT NULL,'
.'data1 VARCHAR(256) NOT NULL'
.');';
$wpdb->get_results($sql);
update_option('jnj_db_version', 1);
Luego una actualización podría ser código fuente como el siguiente:
global $wpdb;
$sql = 'ALTER TABLE jnj_test '
.'ADD COLUMN data2 VARCHAR(256) NOT NULL'
.';';
$wpdb->get_results($sql);
Todo el código fuente en un plugin
Lo siguiente es un simple plugin para WordPress, que podemos poner en un fichero llamado /wp-content/plugins/jnj-test-database.php o /wp-content/plugins/jnj-test-database/jnj-test-database.php por ejemplo. Sólo habrá que ir luego al gestor de plugins del panel de control, y activarlo o desactivarlo para ver los resultados:
<?php
/**
* Plugin Name: Jnj Test Database
* Plugin URI: https://jnjsite.com/
* License: MIT
* Description: For testing purpuoses only
* Version: 0.1
* Author: Jaime Niñoles
* Author URI: https://jnjsite.com/.
*/
defined('ABSPATH') or die('No no no');
define('JNJ_PATH', plugin_dir_path(__FILE__));
class JnjTestDatabase
{
private static $instance;
// HERE SET THE CURRENT VERSION,
// DO NOT DECREASE
private $current_version = 1;
// It uses singleton design pattern..
public static function get_instance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
register_activation_hook(__FILE__, [$this, 'activation']);
register_deactivation_hook(__FILE__, [$this, 'deactivation']);
$this->update_if_needed();
}
public function activation()
{
global $wpdb;
$this->create_initial_tables();
register_setting('jnj_options_group', 'jnj_db_version');
add_option('jnj_db_version', 1);
}
public function deactivation()
{
$this->remove_tables();
unregister_setting('jnj_options_group', 'jnj_db_version');
}
public function create_initial_tables()
{
global $wpdb;
// Main table..
$sql = 'CREATE TABLE jnj_test ('
.'time DATETIME NOT NULL,'
.'data1 VARCHAR(256) NOT NULL'
.');';
$wpdb->get_results($sql);
update_option('jnj_db_version', 1);
}
public function remove_tables()
{
global $wpdb;
$sql = 'DROP TABLE IF EXISTS jnj_test;';
$wpdb->get_results($sql);
}
public function update_if_needed()
{
global $wpdb;
$db_version = get_option('jnj_db_version');
// Updates for v2..
if ($db_version < $this->current_version
and 2 > $db_version) {
$sql = 'ALTER TABLE jnj_test '
.'ADD COLUMN data2 VARCHAR(256) NOT NULL'
.';';
$wpdb->get_results($sql);
++$db_version;
}
// Updates for v3..
if ($db_version < $this->current_version
and 3 > $db_version) {
$sql = 'ALTER TABLE jnj_test '
.'ADD COLUMN data3 VARCHAR(256) NOT NULL'
.';';
$wpdb->get_results($sql);
++$db_version;
}
update_option('jnj_db_version', $this->current_version);
}
}
// Do all..
JnjTestDatabase::get_instance();
Lo único que hace este plugin es crear la variable de WordPress llamada jnj_db_version y crea la tabla con la primera actualización de la BD. Donde pone:
// HERE SET THE CURRENT VERSION,
// DO NOT DECREASE
private $current_version = 1;
..se establece la versión actual de la BD. Hay dos versiones nuevas preparadas para la BD. Si ponemos $current_version = 2 ejecutará la creación inicial y la actualización 2:
// Updates for v2..
if ($db_version < $this->current_version
and 2 > $db_version) {
$sql = 'ALTER TABLE jnj_test '
.'ADD COLUMN data2 VARCHAR(256) NOT NULL'
.';';
$wpdb->get_results($sql);
++$db_version;
}
Y así sucesivamente, podemos ir aumentando de versión mientras que preparamos el script de actualización de la siguiente versión. Si ponemos $current_version = 3 entonces lanzará en la activación la creación (versión 1), la actualización 2 (versión 2) y la actualización 3 (versión 3 de la BD).
Finalmente en la BD se tiene que ver algo tal que así:
Terminando y referencias
Sólo tengo que añadir que en las actualizaciones también se pueden aplicar cambios en los datos, como el movimiento de éstos de una tabla a otra, o su tratamiento. Quizá estamos dividiendo una tabla en dos, o enlazando dos tablas. Es decir, se pueden aplicar todos los cambios que hagan falta también en los datos, no sólo en la estructura mediante SQL. Dejo aquí la documentación oficial que está muy bien para más información:
https://wordpress.org/support/
Otro día más.. ¡un saludo!