Skip to content

Commit

Permalink
Feature migrations up/down (#270)
Browse files Browse the repository at this point in the history
* Migrations up down

* Add forum model

* Syntactic sugar for db structure changes

* Refactor migrations with $up & $down

* Fix migrations upgrade and downgrade

+ Add option to disable auto migrate

* Add migrate:to command

Usage: php aac migrate:to x (x - database version)

* Show error when mail is not enabled

* Fixes regarding to init.php

* Add migrate command to manually upgrade db, incase auto migrate is disabled

* Fixed rest of the migrations

* Limit max version of database

* Don't allow minus number

* Option to clear specified plugin settings by name

* Version is required

* Fix PHPStan errors

* Unset $up after migration, to prevent executing same migration twice

* Add database version to output

* This is not needed

* Update 5.php

* Set database_auto_migrate on install

* Set blank & color only if current db version supports it

* Fix duplicate function declaration
  • Loading branch information
slawkens authored Nov 22, 2024
1 parent 7963628 commit 3f6ff3a
Show file tree
Hide file tree
Showing 72 changed files with 1,268 additions and 396 deletions.
6 changes: 1 addition & 5 deletions aac
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ if(!IS_CLI) {
}

require_once SYSTEM . 'functions.php';
require_once SYSTEM . 'init.php';

define('SELF_NAME', basename(__FILE__));

use MyAAC\Plugins;
use Symfony\Component\Console\Application;

$application = new Application();
$application = new Application('MyAAC', MYAAC_VERSION);

$commandsGlob = glob(SYSTEM . 'src/Commands/*.php');
foreach ($commandsGlob as $item) {
Expand All @@ -34,7 +33,4 @@ foreach ($pluginCommands as $item) {
$application->add(require $item);
}

$application->setName('MyAAC');
$application->setVersion(MYAAC_VERSION);

$application->run();
1 change: 1 addition & 0 deletions install/steps/5-database.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
$configToSave['gzip_output'] = false;
$configToSave['cache_engine'] = 'auto';
$configToSave['cache_prefix'] = 'myaac_' . generateRandomString(8, true, false, true);
$configToSave['database_auto_migrate'] = true;

if(!$error) {
$content = '';
Expand Down
6 changes: 5 additions & 1 deletion system/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

defined('MYAAC') or die('Direct access not allowed!');

global $config;
if(!isset($config['installed']) || !$config['installed']) {
throw new RuntimeException('MyAAC has not been installed yet or there was error during installation. Please install again.');
}
Expand Down Expand Up @@ -142,7 +143,10 @@
}

// execute migrations
require SYSTEM . 'migrate.php';
$configDatabaseAutoMigrate = config('database_auto_migrate');
if (!isset($configDatabaseAutoMigrate) || $configDatabaseAutoMigrate) {
require SYSTEM . 'migrate.php';
}

// settings
$settings = Settings::getInstance();
Expand Down
24 changes: 24 additions & 0 deletions system/libs/pot/OTS_Base_DB.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,30 @@ public function delete($table, $data, $limit = 1)
$this->exec($query);
return true;
}

public function addColumn($table, $column, $definition): void {
$this->exec('ALTER TABLE ' . $this->tableName($table) . ' ADD ' . $this->fieldName($column) . ' ' . $definition . ';');
}

public function modifyColumn($table, $column, $definition): void {
$this->exec('ALTER TABLE ' . $this->tableName($table) . ' MODIFY ' . $this->fieldName($column) . ' ' . $definition . ';');
}

public function changeColumn($table, $from, $to, $definition): void {
$this->exec('ALTER TABLE ' . $this->tableName($table) . ' CHANGE ' . $this->fieldName($from) . ' ' . $this->fieldName($to) . ' ' . $definition . ';');
}

public function dropColumn($table, $column): void {
$this->exec('ALTER TABLE ' . $this->tableName($table) . ' DROP COLUMN ' . $this->fieldName($column) . ';');
}

public function renameTable($from, $to): void {
$this->exec('RENAME TABLE ' . $this->tableName($from) . ' TO ' . $this->tableName($to) . ';');
}

public function dropTable($table, $ifExists = true): void {
$this->exec('DROP TABLE ' . ($ifExists ? 'IF EXISTS' : '') . ' ' . $this->tableName($table) . ';');
}
/**
* LIMIT/OFFSET clause for queries.
*
Expand Down
12 changes: 12 additions & 0 deletions system/migrate.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
$db->revalidateCache();
for($i = $tmp + 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';

if (isset($up)) {
$up();
unset($up);
}

updateDatabaseConfig('database_version', $i);
}
}
Expand All @@ -26,6 +32,12 @@
$db->revalidateCache();
for($i = 1; $i <= DATABASE_VERSION; $i++) {
require SYSTEM . 'migrations/' . $i . '.php';

if (isset($up)) {
$up();
unset($up);
}

updateDatabaseConfig('database_version', $i);
}
}
8 changes: 8 additions & 0 deletions system/migrations/1-hooks.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE `myaac_hooks`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL DEFAULT '',
`type` INT(2) NOT NULL DEFAULT 0,
`file` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
28 changes: 14 additions & 14 deletions system/migrations/1.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?php
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` MODIFY `ip` INT(11) NOT NULL DEFAULT 0;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` MODIFY `date` INT(11) NOT NULL DEFAULT 0;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "account_actions` MODIFY `action` VARCHAR(255) NOT NULL DEFAULT '';");
$db->query("
CREATE TABLE `myaac_hooks`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL DEFAULT '',
`type` INT(2) NOT NULL DEFAULT 0,
`file` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
");
/**
* @var OTS_DB_MySQL $db
*/

?>
$up = function () use ($db) {
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'ip', "INT(11) NOT NULL DEFAULT 0");
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'date', "INT(11) NOT NULL DEFAULT 0");
$db->modifyColumn(TABLE_PREFIX . 'account_actions', 'action', "VARCHAR(255) NOT NULL DEFAULT ''");

$db->query(file_get_contents(__DIR__ . '/1-hooks.sql'));
};

$down = function () use ($db) {
$db->dropTable(TABLE_PREFIX . 'hooks');
};
10 changes: 10 additions & 0 deletions system/migrations/10-admin_menu.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE `myaac_admin_menu`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
`page` VARCHAR(255) NOT NULL DEFAULT '',
`ordering` INT(11) NOT NULL DEFAULT 0,
`flags` INT(11) NOT NULL DEFAULT 0,
`enabled` INT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
37 changes: 22 additions & 15 deletions system/migrations/10.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
<?php
if(!$db->hasColumn(TABLE_PREFIX . 'hooks', 'ordering'))
$db->query("ALTER TABLE `" . TABLE_PREFIX . "hooks` ADD `ordering` INT(11) NOT NULL DEFAULT 0 AFTER `file`;");
/**
* @var OTS_DB_MySQL $db
*/

if(!$db->hasTable(TABLE_PREFIX . 'admin_menu'))
$db->query("
CREATE TABLE `myaac_admin_menu`
(
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
`page` VARCHAR(255) NOT NULL DEFAULT '',
`ordering` INT(11) NOT NULL DEFAULT 0,
`flags` INT(11) NOT NULL DEFAULT 0,
`enabled` INT(1) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
");
$up = function () use ($db) {
if (!$db->hasColumn(TABLE_PREFIX . 'hooks', 'ordering')) {
$db->addColumn(TABLE_PREFIX . 'hooks', 'ordering', "INT(11) NOT NULL DEFAULT 0 AFTER `file`");
}

if (!$db->hasTable(TABLE_PREFIX . 'admin_menu')) {
$db->query(file_get_contents(__DIR__ . '/10-admin_menu.sql'));
}
};

$down = function () use ($db) {
if ($db->hasColumn(TABLE_PREFIX . 'hooks', 'ordering')) {
$db->dropColumn(TABLE_PREFIX . 'hooks', 'ordering');
}

if ($db->hasTable(TABLE_PREFIX . 'admin_menu')) {
$db->dropTable(TABLE_PREFIX . 'admin_menu');
}
};
35 changes: 30 additions & 5 deletions system/migrations/11.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,44 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/

$up = function () use ($db) {
// rename database tables
$db->query("RENAME TABLE
" . TABLE_PREFIX . "screenshots TO " . TABLE_PREFIX . "gallery,
" . TABLE_PREFIX . "movies TO " . TABLE_PREFIX . "videos;");
$db->renameTable(TABLE_PREFIX . 'screenshots', TABLE_PREFIX . 'gallery');
$db->renameTable(TABLE_PREFIX . 'movies', TABLE_PREFIX . 'videos');

// rename images dir
if(file_exists(BASE . 'images/screenshots') && !file_exists(BASE . GALLERY_DIR)) {
if (file_exists(BASE . 'images/screenshots') && !file_exists(BASE . GALLERY_DIR)) {
rename(BASE . 'images/screenshots', BASE . GALLERY_DIR);
}

// convert old database screenshots images to gallery
$query = $db->query('SELECT `id`, `image`, `thumb` FROM `' . TABLE_PREFIX . 'gallery`;');
foreach($query->fetchAll() as $item) {
foreach ($query->fetchAll() as $item) {
$db->update(TABLE_PREFIX . 'gallery', array(
'image' => str_replace('/screenshots/', '/gallery/', $item['image']),
'thumb' => str_replace('/screenshots/', '/gallery/', $item['thumb']),
), array('id' => $item['id']));
}
};

$down = function () use ($db) {
// rename database tables
$db->renameTable(TABLE_PREFIX . 'gallery', TABLE_PREFIX . 'screenshots');
$db->renameTable(TABLE_PREFIX . 'videos', TABLE_PREFIX . 'movies');

// rename images dir
if (file_exists(BASE . GALLERY_DIR) && !file_exists(BASE . 'images/screenshots')) {
rename(BASE . GALLERY_DIR, BASE . 'images/screenshots');
}

// convert new database gallery images to screenshots
$query = $db->query('SELECT `id`, `image`, `thumb` FROM `' . TABLE_PREFIX . 'screenshots`;');
foreach ($query->fetchAll() as $item) {
$db->update(TABLE_PREFIX . 'screenshots', [
'image' => str_replace('/gallery/', '/screenshots/', $item['image']),
'thumb' => str_replace('/gallery/', '/screenshots/', $item['thumb']),
], ['id' => $item['id']]);
}
};
9 changes: 9 additions & 0 deletions system/migrations/12-items.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE `myaac_items`
(
`id` INT(11) NOT NULL,
`article` VARCHAR(5) NOT NULL DEFAULT '',
`name` VARCHAR(50) NOT NULL DEFAULT '',
`plural` VARCHAR(50) NOT NULL DEFAULT '',
`attributes` VARCHAR(500) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
8 changes: 8 additions & 0 deletions system/migrations/12-weapons.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE `myaac_weapons`
(
`id` INT(11) NOT NULL,
`level` INT(11) NOT NULL DEFAULT 0,
`maglevel` INT(11) NOT NULL DEFAULT 0,
`vocations` VARCHAR(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
110 changes: 62 additions & 48 deletions system/migrations/12.php
Original file line number Diff line number Diff line change
@@ -1,51 +1,65 @@
<?php
/**
* @var OTS_DB_MySQL $db
*/

// add new item_id field for runes
if(!$db->hasColumn(TABLE_PREFIX . 'spells', 'item_id'))
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` ADD `item_id` INT(11) NOT NULL DEFAULT 0 AFTER `conjure_count`;");

// change unique index from spell to name
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` DROP INDEX `spell`;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` ADD UNIQUE INDEX (`name`);");

// change comment of spells.type
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` MODIFY `type` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 - instant, 2 - conjure, 3 - rune';");

// new items table
if(!$db->hasTable(TABLE_PREFIX . 'items'))
$db->query("
CREATE TABLE `" . TABLE_PREFIX . "items`
(
`id` INT(11) NOT NULL,
`article` VARCHAR(5) NOT NULL DEFAULT '',
`name` VARCHAR(50) NOT NULL DEFAULT '',
`plural` VARCHAR(50) NOT NULL DEFAULT '',
`attributes` VARCHAR(500) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
");

// new weapons table
if(!$db->hasTable(TABLE_PREFIX . 'weapons'))
$db->query("
CREATE TABLE `" . TABLE_PREFIX . "weapons`
(
`id` INT(11) NOT NULL,
`level` INT(11) NOT NULL DEFAULT 0,
`maglevel` INT(11) NOT NULL DEFAULT 0,
`vocations` VARCHAR(100) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8;
");

// modify vocations to support json data
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` MODIFY `vocations` VARCHAR(100) NOT NULL DEFAULT '';");
$query = $db->query('SELECT `id`, `vocations` FROM `' . TABLE_PREFIX . 'spells`');
foreach($query->fetchAll() as $spell) {
$tmp = explode(',', $spell['vocations']);
foreach($tmp as &$v) {
$v = (int)$v;
use MyAAC\Models\Spell;

$up = function () use ($db) {
// add new item_id field for runes
if (!$db->hasColumn(TABLE_PREFIX . 'spells', 'item_id')) {
$db->addColumn(TABLE_PREFIX . 'spells', 'item_id', 'INT(11) NOT NULL DEFAULT 0 AFTER `conjure_count`');
}

// change unique index from spell to name
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` DROP INDEX `spell`;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` ADD UNIQUE INDEX (`name`);");

// change comment of spells.type
$db->modifyColumn(TABLE_PREFIX . 'spells', 'type', "TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1 - instant, 2 - conjure, 3 - rune'");

// new items table
if (!$db->hasTable(TABLE_PREFIX . 'items')) {
$db->query(file_get_contents(__DIR__ . '/12-items.sql'));
}

// new weapons table
if (!$db->hasTable(TABLE_PREFIX . 'weapons')) {
$db->query(file_get_contents(__DIR__ . '/12-weapons.sql'));
}

// modify vocations to support json data
$db->modifyColumn(TABLE_PREFIX . 'spells', 'vocations', "VARCHAR(100) NOT NULL DEFAULT ''");

$spells = Spell::select('id', 'vocations')->get();
foreach ($spells as $spell) {
$tmp = explode(',', $spell->vocations);
foreach ($tmp as &$v) {
$v = (int)$v;
}

Spell::where('id', $spell->id)->update(['vocations' => json_encode($tmp)]);
}
};

$down = function () use ($db) {
// remove item_id field for runes
if ($db->hasColumn(TABLE_PREFIX . 'spells', 'item_id')) {
$db->dropColumn(TABLE_PREFIX . 'spells', 'item_id');
}

// change unique index from spell to name
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` DROP INDEX `name`;");
$db->query("ALTER TABLE `" . TABLE_PREFIX . "spells` ADD INDEX (`spell`);");

$db->dropTable(TABLE_PREFIX . 'items');
$db->dropTable(TABLE_PREFIX . 'weapons');

$spells = Spell::select('id', 'vocations')->get();
// modify vocations to use vocation separated by comma
foreach ($spells as $spell) {
$vocations = empty($spell->vocations) ? [] : json_decode($spell->vocations);

Spell::where('id', $spell->id)->update(['vocations' => implode(',', $vocations)]);
}
$db->update(TABLE_PREFIX . 'spells', array('vocations' => json_encode($tmp)), array('id' => $spell['id']));
}
?>
};
Loading

0 comments on commit 3f6ff3a

Please sign in to comment.