149 lines
5.1 KiB
PHP
149 lines
5.1 KiB
PHP
<?php
|
|
// DbMigrationManager.php
|
|
// Created: 2023-01-07
|
|
// Updated: 2023-01-07
|
|
|
|
namespace Index\Data\Migration;
|
|
|
|
use stdClass;
|
|
use InvalidArgumentException;
|
|
use DateTimeInterface;
|
|
use Index\DateTime;
|
|
use Index\Data\IDbConnection;
|
|
use Index\Data\IDbStatement;
|
|
use Index\Data\DbType;
|
|
use Index\Data\SQLite\SQLiteConnection;
|
|
|
|
class DbMigrationManager {
|
|
public const DEFAULT_TABLE = 'ndx_migrations';
|
|
|
|
private const CREATE_TRACK_TABLE = 'CREATE TABLE IF NOT EXISTS %1$s (migration_name %2$s PRIMARY KEY, migration_completed %2$s NOT NULL);';
|
|
private const CREATE_TRACK_INDEX = 'CREATE INDEX IF NOT EXISTS %1$s_completed_index ON %1$s (migration_completed);';
|
|
private const DESTROY_TRACK_TABLE = 'DROP TABLE IF EXISTS %s;';
|
|
|
|
private const CHECK_STMT = 'SELECT migration_completed IS NOT NULL FROM %s WHERE migration_name = ?;';
|
|
private const INSERT_STMT = 'INSERT INTO %s (migration_name, migration_completed) VALUES (?, ?);';
|
|
|
|
private const TEMPLATE = <<<EOF
|
|
<?php
|
|
use Index\Data\IDbConnection;
|
|
use Index\Data\Migration\IDbMigration;
|
|
|
|
final class %s implements IDbMigration {
|
|
public function migrate(IDbConnection \$conn): void {
|
|
\$conn->execute('CREATE TABLE ...');
|
|
}
|
|
}
|
|
|
|
EOF;
|
|
|
|
private IDbStatement $checkStmt;
|
|
private IDbStatement $insertStmt;
|
|
|
|
public function __construct(
|
|
private IDbConnection $conn,
|
|
private string $tableName = self::DEFAULT_TABLE,
|
|
) {}
|
|
|
|
public function createTrackingTable(): void {
|
|
// this is not ok but it works for now, there should probably be a generic type bag alias thing
|
|
$nameType = $this->conn instanceof SQLiteConnection ? 'TEXT' : 'VARCHAR(255)';
|
|
|
|
$this->conn->execute(sprintf(self::CREATE_TRACK_TABLE, $this->tableName, $nameType));
|
|
$this->conn->execute(sprintf(self::CREATE_TRACK_INDEX, $this->tableName));
|
|
}
|
|
|
|
public function destroyTrackingTable(): void {
|
|
$this->conn->execute(sprintf(self::DESTROY_TRACK_TABLE, $this->tableName));
|
|
}
|
|
|
|
public function prepareStatements(): void {
|
|
$this->checkStmt = $this->conn->prepare(sprintf(self::CHECK_STMT, $this->tableName));
|
|
$this->insertStmt = $this->conn->prepare(sprintf(self::INSERT_STMT, $this->tableName));
|
|
}
|
|
|
|
public function init(): void {
|
|
$this->createTrackingTable();
|
|
$this->prepareStatements();
|
|
}
|
|
|
|
public function checkMigration(string $name): bool {
|
|
$this->checkStmt->reset();
|
|
$this->checkStmt->addParameter(1, $name, DbType::STRING);
|
|
$this->checkStmt->execute();
|
|
$result = $this->checkStmt->getResult();
|
|
return $result->next() && !$result->isNull(0);
|
|
}
|
|
|
|
public function completeMigration(string $name, ?DateTimeInterface $dateTime = null): void {
|
|
$dateTime = ($dateTime ?? DateTime::utcNow())->format(DateTimeInterface::ATOM);
|
|
$this->insertStmt->reset();
|
|
$this->insertStmt->addParameter(1, $name, DbType::STRING);
|
|
$this->insertStmt->addParameter(2, $dateTime, DbType::STRING);
|
|
$this->insertStmt->execute();
|
|
}
|
|
|
|
public function template(string $name): string {
|
|
return sprintf(self::TEMPLATE, $name);
|
|
}
|
|
|
|
public function createFileName(string $name, ?DateTimeInterface $dateTime = null): string {
|
|
$dateTime ??= DateTime::utcNow();
|
|
|
|
if(empty($name))
|
|
throw new InvalidArgumentException('$name may not be empty.');
|
|
|
|
$name = str_replace(' ', '_', strtolower($name));
|
|
if(!preg_match('#^([a-z_]+)$#', $name))
|
|
throw new InvalidArgumentException('$name may only contain alphabetical, spaces and _ characters.');
|
|
|
|
return $dateTime->format('Y_m_d_His_') . trim($name, '_');
|
|
}
|
|
|
|
public function createClassName(string $name, ?DateTimeInterface $dateTime = null): string {
|
|
$dateTime ??= DateTime::utcNow();
|
|
|
|
if(empty($name))
|
|
throw new InvalidArgumentException('$name may not be empty.');
|
|
|
|
$name = str_replace(' ', '_', strtolower($name));
|
|
if(!preg_match('#^([a-z_]+)$#', $name))
|
|
throw new InvalidArgumentException('$name may only contain alphabetical, spaces and _ characters.');
|
|
|
|
$parts = explode('_', trim($name, '_'));
|
|
$name = '';
|
|
|
|
foreach($parts as $part)
|
|
$name .= ucfirst($part);
|
|
|
|
return $name . $dateTime->format('_Ymd_His');
|
|
}
|
|
|
|
public function createNames(string $baseName, ?DateTimeInterface $dateTime = null): object {
|
|
$dateTime ??= DateTime::utcNow();
|
|
|
|
$names = new stdClass;
|
|
$names->name = $this->createFileName($baseName, $dateTime);
|
|
$names->className = $this->createClassName($baseName, $dateTime);
|
|
|
|
return $names;
|
|
}
|
|
|
|
public function processMigrations(IDbMigrationRepo $migrations): array {
|
|
$migrations = $migrations->getMigrations();
|
|
$completed = [];
|
|
|
|
foreach($migrations as $migration) {
|
|
$name = $migration->getName();
|
|
if($this->checkMigration($name))
|
|
continue;
|
|
|
|
$migration->migrate($this->conn);
|
|
$this->completeMigration($name);
|
|
$completed[] = $name;
|
|
}
|
|
|
|
return $completed;
|
|
}
|
|
}
|