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 <objecttothis@gmail.com>
This commit is contained in:
objec
2026-06-26 16:19:04 +04:00
parent 11ece3f1a3
commit 150cdfb2b8
4 changed files with 136 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class PluginMigrationsTableCreate extends Migration
{
public function up(): void
{
helper('migration');
execute_script(APPPATH . 'Database/Migrations/sqlscripts/3.5.1_PluginMigrationsTableCreate.sql');
}
public function down(): void
{
$this->forge->dropTable('plugin_migrations', true);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Models;
use CodeIgniter\Model;
class PluginMigrationModel extends Model
{
protected $table = 'plugin_migrations';
protected $primaryKey = 'plugin_id';
protected $useAutoIncrement = false;
protected $useSoftDeletes = false;
protected $allowedFields = ['plugin_id', 'version', 'ran_at'];
public function getVersion(string $pluginId): int
{
$row = $this->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]
);
}
}