PHP 回滚数据库迁移

wen PHP项目 3

本文目录导读:

PHP 回滚数据库迁移

  1. 使用 Laravel(最常用)
  2. 使用纯 PHP(手动实现)
  3. 使用第三方库
  4. 重要注意事项
  5. 最佳实践

在 PHP 中实现数据库迁移回滚,主要取决于你使用的迁移工具,以下是几种常见方式的详细说明:

使用 Laravel(最常用)

Laravel 内置了强大的迁移系统,支持回滚操作。

基础命令

# 回滚最后一次迁移
php artisan migrate:rollback
# 回滚指定步数(回滚最后2次)
php artisan migrate:rollback --step=2
# 回滚所有迁移
php artisan migrate:rollback --step=0
# 回滚并重新迁移(常用于开发环境)
php artisan migrate:refresh
# 回滚所有并重新迁移
php artisan migrate:fresh

定义可回滚的迁移

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
    /**
     * 执行迁移
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamps();
        });
    }
    /**
     * 回滚迁移(必须实现)
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

部分回滚

// 仅在特定条件满足时执行回滚
public function down()
{
    if (Schema::hasTable('users')) {
        Schema::table('users', function (Blueprint $table) {
            // 只删除特定列
            $table->dropColumn(['name']);
        });
    }
}

使用纯 PHP(手动实现)

如果不使用框架,可以自己实现简单的迁移回滚机制:

基础迁移类

<?php
interface MigrationInterface
{
    public function up(PDO $pdo);
    public function down(PDO $pdo);
}
abstract class BaseMigration implements MigrationInterface
{
    protected $connection;
    public function __construct(PDO $pdo)
    {
        $this->connection = $pdo;
    }
    protected function execute($sql)
    {
        try {
            $this->connection->exec($sql);
            echo "执行成功: " . date('Y-m-d H:i:s') . "\n";
        } catch (PDOException $e) {
            echo "执行失败: " . $e->getMessage() . "\n";
            throw $e;
        }
    }
    protected function log($message)
    {
        echo "[INFO] " . date('Y-m-d H:i:s') . " - " . $message . "\n";
    }
}

具体迁移实现

<?php
class CreateUsersTableMigration extends BaseMigration
{
    public function up(PDO $pdo)
    {
        $sql = "CREATE TABLE IF NOT EXISTS users (
            id INT PRIMARY KEY AUTO_INCREMENT,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(100) UNIQUE NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
        $this->execute($sql);
        $this->log("用户表创建完成");
    }
    public function down(PDO $pdo)
    {
        $sql = "DROP TABLE IF EXISTS users";
        $this->execute($sql);
        $this->log("用户表已删除");
    }
}

迁移管理器

<?php
class MigrationManager
{
    private $pdo;
    private $migrationsDir;
    private $migrations = [];
    public function __construct(PDO $pdo, $migrationsDir)
    {
        $this->pdo = $pdo;
        $this->migrationsDir = $migrationsDir;
        $this->ensureMigrationsTable();
        $this->loadMigrations();
    }
    private function ensureMigrationsTable()
    {
        $sql = "CREATE TABLE IF NOT EXISTS migration_logs (
            id INT PRIMARY KEY AUTO_INCREMENT,
            migration_name VARCHAR(255) UNIQUE NOT NULL,
            executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )";
        $this->pdo->exec($sql);
    }
    private function loadMigrations()
    {
        $files = glob($this->migrationsDir . '/*.php');
        sort($files);
        foreach ($files as $file) {
            $className = basename($file, '.php');
            require_once $file;
            $this->migrations[$className] = new $className($this->pdo);
        }
    }
    // 执行迁移
    public function migrate()
    {
        foreach ($this->migrations as $name => $migration) {
            // 检查是否已执行
            $stmt = $this->pdo->prepare("SELECT id FROM migration_logs WHERE migration_name = ?");
            $stmt->execute([$name]);
            if (!$stmt->fetch()) {
                echo "正在执行迁移: $name\n";
                $migration->up($this->pdo);
                // 记录执行日志
                $stmt = $this->pdo->prepare("INSERT INTO migration_logs (migration_name) VALUES (?)");
                $stmt->execute([$name]);
                echo "迁移完成: $name\n";
            }
        }
    }
    // 回滚迁移
    public function rollback($steps = 1)
    {
        // 获取最近执行的迁移
        $stmt = $this->pdo->prepare(
            "SELECT migration_name FROM migration_logs 
             ORDER BY id DESC LIMIT ?"
        );
        $stmt->execute([$steps]);
        $executedMigrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
        foreach ($executedMigrations as $name) {
            if (isset($this->migrations[$name])) {
                echo "回滚迁移: $name\n";
                $this->migrations[$name]->down($this->pdo);
                // 删除迁移记录
                $stmt = $this->pdo->prepare("DELETE FROM migration_logs WHERE migration_name = ?");
                $stmt->execute([$name]);
                echo "回滚完成: $name\n";
            }
        }
    }
    // 回滚所有
    public function rollbackAll()
    {
        $stmt = $this->pdo->query("SELECT migration_name FROM migration_logs ORDER BY id DESC");
        $executedMigrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
        foreach ($executedMigrations as $name) {
            if (isset($this->migrations[$name])) {
                echo "回滚迁移: $name\n";
                $this->migrations[$name]->down($this->pdo);
                $stmt = $this->pdo->prepare("DELETE FROM migration_logs WHERE migration_name = ?");
                $stmt->execute([$name]);
                echo "回滚完成: $name\n";
            }
        }
    }
}

使用示例

<?php
// 连接数据库
$pdo = new PDO('mysql:host=localhost;dbname=my_database', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 创建迁移管理器
$manager = new MigrationManager($pdo, __DIR__ . '/migrations');
// 执行迁移
$manager->migrate();
// 回滚最后一次迁移
$manager->rollback();
// 回滚最后3次迁移
$manager->rollback(3);
// 回滚所有迁移
$manager->rollbackAll();

使用第三方库

Doctrine Migrations

composer require doctrine/migrations
<?php
use Doctrine\Migrations\Configuration\Configuration;
use Doctrine\Migrations\DependencyFactory;
use Doctrine\Migrations\Configuration\Connection\ExistingConnection;
use Doctrine\Migrations\Configuration\Migration\PhpFile;
// 配置迁移
$config = new PhpFile('migrations.php');
$connection = new ExistingConnection($pdo);
$dependencyFactory = DependencyFactory::fromConnection($config, $connection);
// 回滚到指定的迁移版本
$dependencyFactory->getMigrator()->rollback('MigrationName');
// 回滚到上一个版本
$dependencyFactory->getMigrator()->rollback(['step' => 1]);

Phinx

composer require robmorgan/phinx
// 回滚命令
vendor/bin/phinx rollback
vendor/bin/phinx rollback -t 20170310101000  // 回滚到指定版本

重要注意事项

  1. 总是实现 down() 方法:每个迁移必须有对应的回滚逻辑
  2. 测试回滚:在生产环境执行回滚前,先在开发环境测试
  3. 备份数据:重要数据回滚前先备份
  4. 确保幂等性:回滚操作应该可以重复执行
  5. 处理复杂关系:考虑外键约束、索引等复杂关系

最佳实践

  • 使用事务:在迁移和回滚中使用数据库事务
  • 记录日志:记录所有迁移和回滚操作
  • 版本控制:迁移文件提交到代码仓库
  • 持续集成:在 CI/CD 流程中集成迁移测试

选择哪种方式取决于项目规模和需求,对于小型项目,手动实现足够;对于大型项目,建议使用 Laravel 或 Doctrine 等成熟方案。

抱歉,评论功能暂时关闭!