From 150cdfb2b881d73a718bd8d5c09c3449f1f9bebc Mon Sep 17 00:00:00 2001 From: objec Date: Fri, 26 Jun 2026 16:19:04 +0400 Subject: [PATCH] Add plugin migration system - Add `runPendingMigrations()` to `PluginManager` for executing plugin-specific migrations. - Create `PluginMigrationModel` for managing plugin migration versions. - Add migration for creating the `plugin_migrations` table to track migration states. Signed-off-by: objec --- ...0627000000_PluginMigrationsTableCreate.php | 19 ++++++ .../3.5.1_PluginMigrationsTableCreate.sql | 6 ++ app/Libraries/Plugins/PluginManager.php | 68 +++++++++++++++++++ app/Models/PluginMigrationModel.php | 43 ++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 app/Database/Migrations/20260627000000_PluginMigrationsTableCreate.php create mode 100644 app/Database/Migrations/sqlscripts/3.5.1_PluginMigrationsTableCreate.sql create mode 100644 app/Models/PluginMigrationModel.php diff --git a/app/Database/Migrations/20260627000000_PluginMigrationsTableCreate.php b/app/Database/Migrations/20260627000000_PluginMigrationsTableCreate.php new file mode 100644 index 000000000..a1325819e --- /dev/null +++ b/app/Database/Migrations/20260627000000_PluginMigrationsTableCreate.php @@ -0,0 +1,19 @@ +forge->dropTable('plugin_migrations', true); + } +} diff --git a/app/Database/Migrations/sqlscripts/3.5.1_PluginMigrationsTableCreate.sql b/app/Database/Migrations/sqlscripts/3.5.1_PluginMigrationsTableCreate.sql new file mode 100644 index 000000000..20e1d19f4 --- /dev/null +++ b/app/Database/Migrations/sqlscripts/3.5.1_PluginMigrationsTableCreate.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS `ospos_plugin_migrations` ( + `plugin_id` varchar(100) NOT NULL, + `version` bigint(20) UNSIGNED NOT NULL DEFAULT 0, + `ran_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`plugin_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/app/Libraries/Plugins/PluginManager.php b/app/Libraries/Plugins/PluginManager.php index 1ae3ae964..9061816e0 100644 --- a/app/Libraries/Plugins/PluginManager.php +++ b/app/Libraries/Plugins/PluginManager.php @@ -3,6 +3,7 @@ namespace App\Libraries\Plugins; use App\Models\PluginConfig; +use App\Models\PluginMigrationModel; use CodeIgniter\Events\Events; use Config\Database; use Config\Services; @@ -82,6 +83,8 @@ class PluginManager return; } + $this->runPendingMigrations(); + foreach ($this->plugins as $pluginId => $plugin) { if ($this->isPluginEnabled($pluginId)) { $this->enabledPlugins[$pluginId] = $plugin; @@ -93,6 +96,71 @@ class PluginManager $this->eventsRegistered = true; } + private function runPendingMigrations(): void + { + if (session()->get('plugin_migrations_ran')) { + return; + } + + $migrationModel = new PluginMigrationModel(); + $db = Database::connect(); + $forge = Database::forge(); + + foreach ($this->plugins as $pluginId => $plugin) { + if (!$this->isPluginEnabled($pluginId)) { + continue; + } + + $parts = explode('\\', get_class($plugin)); + if (count($parts) < 4) { + continue; + } + + $pluginDirName = $parts[2]; + $migrationsPath = APPPATH . "Plugins/{$pluginDirName}/Migrations/"; + + if (!is_dir($migrationsPath)) { + continue; + } + + $files = glob($migrationsPath . '*.php') ?: []; + $migrationFiles = array_filter($files, static fn($f) => preg_match('/\/\d{14}_/', $f)); + sort($migrationFiles); + + $currentVersion = $migrationModel->getVersion($pluginId); + + foreach ($migrationFiles as $file) { + $basename = basename($file, '.php'); + $timestamp = (int) substr($basename, 0, 14); + + if ($timestamp <= $currentVersion) { + continue; + } + + $className = substr($basename, 15); // strip "20260627120000_" + $fqcn = "App\\Plugins\\{$pluginDirName}\\Migrations\\{$className}"; + + require_once $file; + + if (!class_exists($fqcn)) { + log_message('error', "Plugin migration class not found: {$fqcn}"); + break; + } + + try { + (new $fqcn($db, $forge))->up(); + $migrationModel->setVersion($pluginId, $timestamp); + log_message('info', "Plugin migration ran: {$pluginId} v{$timestamp}"); + } catch (Throwable $e) { + log_message('error', "Plugin migration failed: {$pluginId} v{$timestamp}: " . $e->getMessage()); + break; + } + } + } + + session()->set('plugin_migrations_ran', true); + } + public function getAllPlugins(): array { return $this->plugins; diff --git a/app/Models/PluginMigrationModel.php b/app/Models/PluginMigrationModel.php new file mode 100644 index 000000000..d854a1d88 --- /dev/null +++ b/app/Models/PluginMigrationModel.php @@ -0,0 +1,43 @@ +db->table('plugin_migrations') + ->where('plugin_id', $pluginId) + ->get() + ->getRow(); + + return $row ? (int) $row->version : 0; + } + + public function setVersion(string $pluginId, int $version): bool + { + $builder = $this->db->table('plugin_migrations'); + $exists = $builder->where('plugin_id', $pluginId)->countAllResults() > 0; + + if (!$exists) { + return $builder->insert([ + 'plugin_id' => $pluginId, + 'version' => $version, + 'ran_at' => date('Y-m-d H:i:s'), + ]); + } + + return $builder->update( + ['version' => $version, 'ran_at' => date('Y-m-d H:i:s')], + ['plugin_id' => $pluginId] + ); + } +}