Как реализовать обновление WordPress плагина с изменением базы данных?
Я разрабатываю WordPress плагин, который использует несколько собственных таблиц в базе данных. Плагин создает эти таблицы при активации и удаляет их при деактивации/удалении.
Мне необходимо реализовать процесс обновления плагина, который будет обновлять как код плагина, так и структуру таблиц. Самый простой случай - добавление новой колонки в одну из таблиц. Более сложный случай - создание новой структуры таблиц и соответствующее обновление данных.
Как вы рекомендуете решить эту проблему? Есть ли в WordPress встроенные функции, которые могут помочь?

Короткий ответ — да, класс $wpdb
. См. Codex для получения дополнительной информации.
Каждый раз, когда вы взаимодействуете с пользовательской таблицей (или любой таблицей вообще), вы должны использовать $wpdb
— особенно убедитесь, что знакомы с методом prepare
, который помогает экранировать запросы и предотвращать инъекции.
Вы уже должны быть знакомы с этим, так как используете его для создания таблицы. В хуке установки у вас должно быть что-то вроде:
$charset_collate = '';
if ( ! empty($wpdb->charset) )
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
if ( ! empty($wpdb->collate) )
$charset_collate .= " COLLATE $wpdb->collate";
// Создаем пользовательскую таблицу
$sql_custom_table ="CREATE TABLE {$wpdb->prefix}my_table (
id bigint(20) unsigned NOT NULL auto_increment,
column_a varchar(255) default NULL,
column_b varchar(255) default NULL,
PRIMARY KEY (id)
) $charset_collate; ";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_custom_table);
Этот код фактически выполняется каждый раз, когда плагин активируется (т.е. не только при установке). Таким образом, он будет запускаться, когда кто-то обновляет плагин автоматически. Примечание: Если обновление происходит вручную путем замены файлов плагина — код не выполнится, поэтому вам нужно будет запустить этот код через admin_init
при обновлении плагина (сохраняйте номер версии в таблице опций и сравнивайте с текущей версией).
Обычно вам не нужно, чтобы SQL-команда CREATE TABLE
выполнялась при каждом обновлении плагина — здесь на помощь приходит dbDelta()
.
Перед выполнением команды он проверяет, существует ли таблица. Более того, он проверяет типы столбцов. Если таблица не существует, она создается; если существует, но типы столбцов изменились — они обновляются; если столбец отсутствует — он добавляется.
К сожалению, если вы удалите столбец из кода выше, он не будет автоматически удален из таблицы. Для удаления столбцов или таблиц нужно явно использовать DROP
(предварительно убедившись, что они существуют).

Правильный способ сделать это в настоящее время - включить вашу схему в виде файла в исходный код плагина и использовать встроенную функцию WordPress dbDelta() для обновления базы данных по мере необходимости с использованием этой схемы. Фактический требуемый код очень прост:
$sql = file_get_contents( plugin_dir_path(__FILE__) . "/schema.sql" );
dbDelta( $sql );
Это позволит как создать, так и обновить базу данных при необходимости. На момент последней проверки функция не удаляет старые неиспользуемые столбцы, поэтому вам нужно будет реализовать это через проверку версии. Это прекрасная функция WordPress, которая экономит массу времени. Будьте внимательны при создании файла schema.sql - копируйте пробелы из экспорта схемы MySQL точно, так как dbDelta() известна своей придирчивостью к пробелам. Также следует проверять версию базы данных и, если она не актуальна, вызывать указанный выше код для обновления. Возможно, вам также потребуется выполнить специфичные обновления для изменений, которые dbDelta() не обрабатывает правильно (например, удаление столбца). Легко написать простую логическую проверку if, чтобы определить, обновилась ли версия, и выполнить эти ручные обновления через $wpdb. Например, вы можете удалить столбец, который больше не используется.
$installed_ver = get_option(MY_DB_VERSION);
$wpp = $wpdb->prefix . "mypluginname";
if ($installed_ver < 102)
$wpdb->query("ALTER TABLE ${wpp}_movies DROP nft_date");
if ($installed_ver < 107)
$wpdb->query("ALTER TABLE ${wpp}_movies CHANGE lastupdated "
. "lastupdated TIMESTAMP on update CURRENT_TIMESTAMP "
. "NOT NULL DEFAULT CURRENT_TIMESTAMP");
update_option(MY_DB_VERSION, $db_version);
Это упрощенная версия рабочего кода, приношу извинения, если что-то сломал в процессе упрощения для публикации.
Также учтите, что начиная с WordPress 3.9.2, WordPress не всегда запускает хук активации при обновлении плагина (в частности, если массовое обновление выполняется со страницы "Обновления" в админ-панели).

В последнее время я начал брать версию БД из времени модификации файла schema.sql. Это означает, что простое обновление файла schema.sql достаточно для обновления базы данных; не нужно помнить о редактировании версии БД. Что-то вроде: $db_version = filemtime("schema.sql");

То есть, если время файла изменится из-за внешних факторов, например, при переносе серверов, изменится mtime и версия БД?

Время файла всегда в GMT, и серверы редко отличаются на несколько секунд, поэтому почти невозможно, чтобы это сработало дважды из-за этого. Однако, даже если это произойдет снова, никакого вреда не будет, так как скрипт запустится один раз и сравнит с текущей БД, очевидно, ничего не изменяя. В этом прекрасная особенность dbDelta() — она может выполняться многократно без проблем. Хороший вопрос, спасибо.
