307 lines
9.5 KiB
PHP
307 lines
9.5 KiB
PHP
<?php
|
|
/**
|
|
* Advanced Database Migration Runner for API Module
|
|
* Supports both upgrades and rollbacks
|
|
*/
|
|
|
|
// Define necessary constants
|
|
define('BASEPATH', true);
|
|
define('APPPATH', '../application/');
|
|
define('ENVIRONMENT', 'production');
|
|
|
|
class APIMigrationRunner
|
|
{
|
|
private $CI;
|
|
private $module_name = 'api';
|
|
private $migration_path = 'migrations/';
|
|
|
|
public function __construct()
|
|
{
|
|
// Load Perfex CRM framework
|
|
require_once '../../index.php';
|
|
$this->CI =& get_instance();
|
|
}
|
|
|
|
public function run($target_version = null, $rollback = false)
|
|
{
|
|
$this->print_header();
|
|
|
|
// Validate environment
|
|
if (!$this->validate_environment()) {
|
|
return false;
|
|
}
|
|
|
|
// Get current version
|
|
$current_version = $this->get_current_version();
|
|
echo "📊 Current migration version: $current_version\n\n";
|
|
|
|
// Get available migrations
|
|
$migrations = $this->get_available_migrations();
|
|
|
|
if ($rollback) {
|
|
return $this->run_rollback($current_version, $target_version, $migrations);
|
|
} else {
|
|
return $this->run_upgrade($current_version, $target_version, $migrations);
|
|
}
|
|
}
|
|
|
|
private function validate_environment()
|
|
{
|
|
// Check if API module is active
|
|
if (!$this->CI->app_modules->is_active($this->module_name)) {
|
|
echo "❌ ERROR: API module is not active. Please activate it first.\n";
|
|
echo " Go to Admin → Setup → Modules and activate the API module.\n\n";
|
|
return false;
|
|
}
|
|
|
|
echo "✅ API module is active\n";
|
|
|
|
// Check database connection
|
|
if (!isset($this->CI->db) || !$this->CI->db->conn_id) {
|
|
echo "❌ ERROR: Database connection failed\n";
|
|
return false;
|
|
}
|
|
|
|
echo "✅ Database connection established\n";
|
|
return true;
|
|
}
|
|
|
|
private function get_current_version()
|
|
{
|
|
$migration_table = db_prefix() . 'migrations';
|
|
|
|
if ($this->CI->db->table_exists('migrations')) {
|
|
$result = $this->CI->db->select('version')
|
|
->from('migrations')
|
|
->where('module', $this->module_name)
|
|
->get();
|
|
|
|
if ($result->num_rows() > 0) {
|
|
return $result->row()->version;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private function get_available_migrations()
|
|
{
|
|
$migrations = [];
|
|
|
|
// Scan migration files
|
|
$files = glob($this->migration_path . '*_version_*.php');
|
|
|
|
foreach ($files as $file) {
|
|
if (preg_match('/(\d+)_version_(\d+)\.php$/', basename($file), $matches)) {
|
|
$version = (int) $matches[1];
|
|
$migrations[$version] = [
|
|
'file' => $file,
|
|
'class' => 'Migration_Version_' . $matches[1]
|
|
];
|
|
}
|
|
}
|
|
|
|
ksort($migrations);
|
|
return $migrations;
|
|
}
|
|
|
|
private function run_upgrade($current_version, $target_version, $migrations)
|
|
{
|
|
// Determine which migrations to run
|
|
$migrations_to_run = [];
|
|
foreach ($migrations as $version => $migration) {
|
|
if ($version > $current_version && ($target_version === null || $version <= $target_version)) {
|
|
$migrations_to_run[$version] = $migration;
|
|
}
|
|
}
|
|
|
|
if (empty($migrations_to_run)) {
|
|
echo "✅ All migrations are up to date!\n\n";
|
|
return true;
|
|
}
|
|
|
|
echo "📋 Migrations to run:\n";
|
|
foreach ($migrations_to_run as $version => $migration) {
|
|
echo " - Version $version: {$migration['class']}\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Confirm execution
|
|
if (!$this->confirm_execution()) {
|
|
return false;
|
|
}
|
|
|
|
// Execute migrations
|
|
return $this->execute_migrations($migrations_to_run, 'up');
|
|
}
|
|
|
|
private function run_rollback($current_version, $target_version, $migrations)
|
|
{
|
|
// Determine rollback target
|
|
$target = $target_version ?? 0;
|
|
|
|
if ($target >= $current_version) {
|
|
echo "❌ ERROR: Target version ($target) must be less than current version ($current_version)\n";
|
|
return false;
|
|
}
|
|
|
|
// Find migrations to rollback
|
|
$migrations_to_rollback = [];
|
|
foreach (array_reverse($migrations, true) as $version => $migration) {
|
|
if ($version > $target && $version <= $current_version) {
|
|
$migrations_to_rollback[$version] = $migration;
|
|
}
|
|
}
|
|
|
|
if (empty($migrations_to_rollback)) {
|
|
echo "✅ No migrations to rollback!\n\n";
|
|
return true;
|
|
}
|
|
|
|
echo "📋 Migrations to rollback:\n";
|
|
foreach ($migrations_to_rollback as $version => $migration) {
|
|
echo " - Version $version: {$migration['class']}\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Confirm execution
|
|
if (!$this->confirm_execution('rollback')) {
|
|
return false;
|
|
}
|
|
|
|
// Execute rollbacks
|
|
return $this->execute_migrations($migrations_to_rollback, 'down');
|
|
}
|
|
|
|
private function confirm_execution($action = 'upgrade')
|
|
{
|
|
if (php_sapi_name() === 'cli') {
|
|
echo "Do you want to $action these migrations? (y/N): ";
|
|
$handle = fopen("php://stdin", "r");
|
|
$response = trim(fgets($handle));
|
|
fclose($handle);
|
|
|
|
if (strtolower($response) !== 'y' && strtolower($response) !== 'yes') {
|
|
echo "Migration $action cancelled.\n";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function execute_migrations($migrations, $method)
|
|
{
|
|
$success_count = 0;
|
|
$error_count = 0;
|
|
|
|
foreach ($migrations as $version => $migration) {
|
|
echo "🔄 Running migration {$migration['class']}::$method()...\n";
|
|
|
|
try {
|
|
// Load migration file
|
|
if (!file_exists($migration['file'])) {
|
|
throw new Exception("Migration file not found: {$migration['file']}");
|
|
}
|
|
|
|
require_once $migration['file'];
|
|
|
|
// Instantiate migration class
|
|
if (!class_exists($migration['class'])) {
|
|
throw new Exception("Migration class not found: {$migration['class']}");
|
|
}
|
|
|
|
$migration_instance = new $migration['class']();
|
|
|
|
// Run the method
|
|
if (!method_exists($migration_instance, $method)) {
|
|
throw new Exception("Method $method() not found in migration class");
|
|
}
|
|
|
|
$migration_instance->$method();
|
|
echo " ✅ Migration completed successfully\n";
|
|
|
|
// Update migration version in database
|
|
if ($method === 'up') {
|
|
$this->CI->db->replace('migrations', [
|
|
'module' => $this->module_name,
|
|
'version' => $version
|
|
]);
|
|
} elseif ($method === 'down') {
|
|
// For rollback, set to previous version
|
|
$prev_version = $this->get_previous_version($version, $migrations);
|
|
if ($prev_version === null) {
|
|
$this->CI->db->delete('migrations', ['module' => $this->module_name]);
|
|
} else {
|
|
$this->CI->db->replace('migrations', [
|
|
'module' => $this->module_name,
|
|
'version' => $prev_version
|
|
]);
|
|
}
|
|
}
|
|
|
|
$success_count++;
|
|
|
|
} catch (Exception $e) {
|
|
echo " ❌ Migration failed: " . $e->getMessage() . "\n";
|
|
$error_count++;
|
|
}
|
|
|
|
echo "\n";
|
|
}
|
|
|
|
// Summary
|
|
$action_name = ($method === 'up') ? 'upgrade' : 'rollback';
|
|
echo "📊 Migration Summary ($action_name):\n";
|
|
echo " ✅ Successful: $success_count\n";
|
|
echo " ❌ Failed: $error_count\n\n";
|
|
|
|
if ($error_count > 0) {
|
|
echo "⚠️ Some migrations failed. Please check the errors above.\n";
|
|
return false;
|
|
} else {
|
|
echo "🎉 All migrations completed successfully!\n";
|
|
echo " API module database is now " . ($method === 'up' ? 'up to date' : 'rolled back') . ".\n";
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private function get_previous_version($current_version, $migrations)
|
|
{
|
|
$versions = array_keys($migrations);
|
|
sort($versions);
|
|
|
|
$index = array_search($current_version, $versions);
|
|
if ($index === false || $index === 0) {
|
|
return null;
|
|
}
|
|
|
|
return $versions[$index - 1];
|
|
}
|
|
|
|
private function print_header()
|
|
{
|
|
echo "🚀 API Module Database Migration Runner\n";
|
|
echo "=======================================\n\n";
|
|
}
|
|
}
|
|
|
|
// CLI Interface
|
|
if (php_sapi_name() === 'cli') {
|
|
$options = getopt('t:r', ['target:', 'rollback']);
|
|
|
|
$target_version = isset($options['t']) ? (int)$options['t'] : (isset($options['target']) ? (int)$options['target'] : null);
|
|
$rollback = isset($options['r']) || isset($options['rollback']);
|
|
|
|
$runner = new APIMigrationRunner();
|
|
$success = $runner->run($target_version, $rollback);
|
|
|
|
exit($success ? 0 : 1);
|
|
} else {
|
|
// Web interface
|
|
echo "<pre>";
|
|
$runner = new APIMigrationRunner();
|
|
$runner->run();
|
|
echo "</pre>";
|
|
}
|
|
?>
|