58 lines
1.7 KiB
PHP
58 lines
1.7 KiB
PHP
|
|
<?php
|
||
|
|
namespace App\Services;
|
||
|
|
|
||
|
|
use PDO;
|
||
|
|
|
||
|
|
/** Applies ordered *.sql files from a migrations dir, tracked in a `migrations` table. */
|
||
|
|
class Migrator
|
||
|
|
{
|
||
|
|
private PDO $pdo;
|
||
|
|
private string $dir;
|
||
|
|
|
||
|
|
public function __construct(PDO $pdo, string $dir)
|
||
|
|
{
|
||
|
|
$this->pdo = $pdo;
|
||
|
|
$this->dir = $dir;
|
||
|
|
}
|
||
|
|
|
||
|
|
public function ensureTable(): void
|
||
|
|
{
|
||
|
|
$this->pdo->exec(
|
||
|
|
"CREATE TABLE IF NOT EXISTS `migrations` (
|
||
|
|
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||
|
|
`filename` VARCHAR(255) NOT NULL UNIQUE,
|
||
|
|
`applied_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return string[] filenames already applied */
|
||
|
|
public function applied(): array
|
||
|
|
{
|
||
|
|
return $this->pdo->query("SELECT filename FROM `migrations`")->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return string[] absolute paths of pending migrations, in order */
|
||
|
|
public function pending(): array
|
||
|
|
{
|
||
|
|
$all = glob($this->dir . '/*.sql') ?: [];
|
||
|
|
sort($all);
|
||
|
|
$applied = $this->applied();
|
||
|
|
return array_values(array_filter($all, fn($p) => !in_array(basename($p), $applied, true)));
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @return string[] filenames applied this run */
|
||
|
|
public function migrate(): array
|
||
|
|
{
|
||
|
|
$this->ensureTable();
|
||
|
|
$done = [];
|
||
|
|
foreach ($this->pending() as $path) {
|
||
|
|
$this->pdo->exec((string) file_get_contents($path));
|
||
|
|
$stmt = $this->pdo->prepare("INSERT IGNORE INTO `migrations` (filename) VALUES (?)");
|
||
|
|
$stmt->execute([basename($path)]);
|
||
|
|
$done[] = basename($path);
|
||
|
|
}
|
||
|
|
return $done;
|
||
|
|
}
|
||
|
|
}
|