- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
117 lines
4.2 KiB
PHP
117 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Database\Migration\Services;
|
|
|
|
use App\Framework\Database\ConnectionInterface;
|
|
use App\Framework\Database\Migration\Migration;
|
|
use App\Framework\Database\Migration\ValueObjects\MigrationTableConfig;
|
|
use App\Framework\Database\Platform\DatabasePlatform;
|
|
use App\Framework\Database\ValueObjects\SqlQuery;
|
|
use App\Framework\DateTime\Clock;
|
|
|
|
final readonly class MigrationDatabaseManager
|
|
{
|
|
public function __construct(
|
|
private ConnectionInterface $connection,
|
|
private DatabasePlatform $platform,
|
|
private Clock $clock,
|
|
private MigrationTableConfig $tableConfig
|
|
) {
|
|
}
|
|
|
|
public function ensureMigrationsTable(): void
|
|
{
|
|
if ($this->tableExists($this->tableConfig->tableName)) {
|
|
return;
|
|
}
|
|
|
|
$sql = $this->createMigrationsTableSQL($this->tableConfig->tableName);
|
|
|
|
// PostgreSQL doesn't support multiple statements in prepared statements
|
|
// Split by semicolon and execute each statement separately
|
|
if ($this->platform->getName() === 'PostgreSQL') {
|
|
$statements = array_filter(
|
|
array_map('trim', explode(';', $sql)),
|
|
fn($stmt) => !empty($stmt)
|
|
);
|
|
|
|
foreach ($statements as $statement) {
|
|
$this->connection->execute(SqlQuery::create($statement));
|
|
}
|
|
} else {
|
|
$this->connection->execute(SqlQuery::create($sql));
|
|
}
|
|
}
|
|
|
|
public function recordMigrationExecution(Migration $migration, string $version): void
|
|
{
|
|
$now = $this->clock->now()->format('Y-m-d H:i:s');
|
|
|
|
$sql = "INSERT INTO {$this->tableConfig->tableName} (version, description, executed_at) VALUES (?, ?, ?)";
|
|
$this->connection->execute(SqlQuery::create($sql, [$version, $migration->getDescription(), $now]));
|
|
}
|
|
|
|
public function recordMigrationRollback(string $version): void
|
|
{
|
|
$sql = "DELETE FROM {$this->tableConfig->tableName} WHERE version = ?";
|
|
$this->connection->execute(SqlQuery::create($sql, [$version]));
|
|
}
|
|
|
|
public function getAppliedVersions(): array
|
|
{
|
|
$this->ensureMigrationsTable();
|
|
|
|
$sql = "SELECT version FROM {$this->tableConfig->tableName} ORDER BY executed_at ASC";
|
|
|
|
return $this->connection->queryColumn(SqlQuery::create($sql));
|
|
}
|
|
|
|
public function tableExists(string $tableName): bool
|
|
{
|
|
try {
|
|
$sql = $this->platform->getTableExistsSQL($tableName);
|
|
$result = $this->connection->queryScalar(SqlQuery::create($sql));
|
|
|
|
return (bool) $result;
|
|
} catch (\Throwable $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function createMigrationsTableSQL(string $tableName): string
|
|
{
|
|
return match ($this->platform->getName()) {
|
|
'mysql' => "CREATE TABLE {$tableName} (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
version VARCHAR(255) NOT NULL UNIQUE,
|
|
description TEXT NOT NULL,
|
|
executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
INDEX idx_version (version),
|
|
INDEX idx_executed_at (executed_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
|
|
|
|
'PostgreSQL' => "CREATE TABLE {$tableName} (
|
|
id SERIAL PRIMARY KEY,
|
|
version VARCHAR(255) NOT NULL UNIQUE,
|
|
description TEXT NOT NULL,
|
|
executed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX idx_{$tableName}_version ON {$tableName} (version);
|
|
CREATE INDEX idx_{$tableName}_executed_at ON {$tableName} (executed_at);",
|
|
|
|
'sqlite' => "CREATE TABLE {$tableName} (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
version TEXT NOT NULL UNIQUE,
|
|
description TEXT NOT NULL,
|
|
executed_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
CREATE INDEX idx_{$tableName}_version ON {$tableName} (version);
|
|
CREATE INDEX idx_{$tableName}_executed_at ON {$tableName} (executed_at);",
|
|
|
|
default => throw new \RuntimeException("Unsupported database platform: {$this->platform->getName()}")
|
|
};
|
|
}
|
|
}
|