| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- <?php
- declare(strict_types=1);
- namespace Glacier\Core;
- use PDO;
- /**
- * SQL 迁移执行器:database/ 下 0001_xxx.sql 顺序执行(单库多表)。
- * 执行记录写入 g_migrations 表,重复执行幂等(事务内原子应用)。
- */
- final class Migrator
- {
- private PDO $pdo;
- private string $dir;
- public function __construct(PDO $pdo, string $dir)
- {
- $this->pdo = $pdo;
- $this->dir = rtrim($dir, '/\\');
- }
- /**
- * 应用所有未执行的迁移,返回本次执行的迁移名列表。
- * 注意:MySQL/MariaDB 的 DDL(CREATE/ALTER)会隐式提交,事务包裹无意义,
- * 故直接顺序执行;迁移 SQL 遵循幂等约定(IF NOT EXISTS / ON DUPLICATE KEY 等),
- * 失败重跑安全。
- */
- public function up(): array
- {
- $this->ensureTable();
- $applied = $this->applied();
- $executed = [];
- foreach ($this->files() as $name => $sql) {
- if (isset($applied[$name])) {
- continue;
- }
- try {
- $this->pdo->exec($sql);
- $stmt = $this->pdo->prepare('INSERT INTO g_migrations (name) VALUES (?)');
- $stmt->execute([$name]);
- $executed[] = $name;
- } catch (\Throwable $e) {
- throw new \RuntimeException("迁移 {$name} 失败: " . $e->getMessage(), 0, $e);
- }
- }
- return $executed;
- }
- /** 全部迁移的应用状态。 */
- public function status(): array
- {
- $this->ensureTable();
- $applied = $this->applied();
- $result = [];
- foreach ($this->files() as $name => $sql) {
- $result[] = ['name' => $name, 'applied' => isset($applied[$name])];
- }
- return $result;
- }
- /** @return array<string,string> 按编号排序的迁移 SQL。 */
- private function files(): array
- {
- $files = [];
- foreach (glob($this->dir . '/*.sql') ?: [] as $file) {
- if (str_ends_with($file, '.down.sql')) {
- continue; // 回滚文件由 down 流程单独处理(本期未启用)
- }
- $files[basename($file)] = (string) file_get_contents($file);
- }
- ksort($files);
- return $files;
- }
- /** @return array<string,bool> 已应用迁移名集合。 */
- private function applied(): array
- {
- $rows = $this->pdo->query('SELECT name FROM g_migrations')->fetchAll(PDO::FETCH_COLUMN);
- return array_fill_keys($rows, true);
- }
- private function ensureTable(): void
- {
- $this->pdo->exec(
- "CREATE TABLE IF NOT EXISTS g_migrations (
- id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
- name VARCHAR(255) NOT NULL UNIQUE,
- applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
- );
- }
- }
|