本文目录导读:

我来详细介绍PHP项目实现数据迁移工具的几种主流方案和实践方法。
使用成熟的数据迁移库
Phinx (推荐)
最流行的PHP数据迁移库,支持多种数据库。
// 安装
composer require robmorgan/phinx
// 创建迁移文件
vendor/bin/phinx create MyNewMigration
// 示例迁移文件
<?php
use Phinx\Migration\AbstractMigration;
class CreateUsersTable extends AbstractMigration
{
public function up()
{
$table = $this->table('users', ['id' => false, 'primary_key' => ['id']]);
$table->addColumn('id', 'integer', ['signed' => false, 'identity' => true])
->addColumn('username', 'string', ['limit' => 100])
->addColumn('email', 'string')
->addColumn('password', 'string')
->addColumn('created_at', 'datetime')
->addColumn('updated_at', 'datetime')
->create();
}
public function down()
{
$this->dropTable('users');
}
}
Laravel Migration (Laravel框架)
// Laravel框架内置
php artisan make:migration create_users_table
// 示例
<?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->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('users');
}
}
手动实现轻量级迁移工具
<?php
class SimpleMigration
{
private $pdo;
private $migrationsPath;
private $migrationsTable = 'migrations';
public function __construct(PDO $pdo, string $migrationsPath)
{
$this->pdo = $pdo;
$this->migrationsPath = $migrationsPath;
}
public function initialize()
{
// 创建迁移记录表
$this->pdo->exec("
CREATE TABLE IF NOT EXISTS {$this->migrationsTable} (
id INT AUTO_INCREMENT PRIMARY KEY,
migration VARCHAR(255) NOT NULL,
batch INT NOT NULL,
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
}
public function migrate()
{
$this->initialize();
// 获取已执行的迁移
$executed = $this->getExecutedMigrations();
$batch = $this->getNextBatchNumber();
// 获取所有迁移文件
$files = glob($this->migrationsPath . '/*.php');
sort($files);
foreach ($files as $file) {
$migrationName = basename($file, '.php');
if (!in_array($migrationName, $executed)) {
// 执行迁移
require_once $file;
$className = $this->getClassName($migrationName);
$migration = new $className($this->pdo);
echo "Running migration: {$migrationName}\n";
$migration->up();
// 记录执行状态
$stmt = $this->pdo->prepare(
"INSERT INTO {$this->migrationsTable} (migration, batch) VALUES (?, ?)"
);
$stmt->execute([$migrationName, $batch]);
}
}
}
public function rollback()
{
// 获取最后一批迁移
$stmt = $this->pdo->prepare(
"SELECT migration FROM {$this->migrationsTable} WHERE batch = (
SELECT MAX(batch) FROM {$this->migrationsTable}
) ORDER BY id DESC"
);
$stmt->execute();
$lastBatch = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach ($lastBatch as $migrationName) {
require_once $this->migrationsPath . '/' . $migrationName . '.php';
$className = $this->getClassName($migrationName);
$migration = new $className($this->pdo);
echo "Rolling back: {$migrationName}\n";
$migration->down();
// 删除记录
$this->pdo->prepare(
"DELETE FROM {$this->migrationsTable} WHERE migration = ?"
)->execute([$migrationName]);
}
}
private function getExecutedMigrations(): array
{
$stmt = $this->pdo->query(
"SELECT migration FROM {$this->migrationsTable} ORDER BY id ASC"
);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
private function getNextBatchNumber(): int
{
$stmt = $this->pdo->query(
"SELECT COALESCE(MAX(batch), 0) + 1 FROM {$this->migrationsTable}"
);
return (int) $stmt->fetchColumn();
}
private function getClassName(string $migrationName): string
{
// 移除时间戳,转换为类名
$parts = preg_split('/_/', $migrationName, 2);
return 'Migration_' . str_replace(' ', '', ucwords(str_replace('_', ' ', $parts[1] ?? $parts[0])));
}
}
// 使用示例
try {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'password');
$migration = new SimpleMigration($pdo, __DIR__ . '/migrations');
// 执行迁移
$migration->migrate();
// 回滚
// $migration->rollback();
} catch (PDOException $e) {
echo "Database error: " . $e->getMessage() . "\n";
}
迁移文件示例
<?php
// migrations/20210101000001_create_users_table.php
class Migration_CreateUsersTable
{
private $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function up()
{
$this->pdo->exec("
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
is_active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
}
public function down()
{
$this->pdo->exec("DROP TABLE IF EXISTS users");
}
}
数据库种子数据功能
<?php
// seeds/AdminUserSeeder.php
class AdminUserSeeder
{
private $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function run()
{
$stmt = $this->pdo->prepare("
INSERT INTO users (username, email, password, role)
VALUES (?, ?, ?, ?)
");
$stmt->execute([
'admin',
'admin@example.com',
password_hash('admin123', PASSWORD_BCRYPT),
'admin'
]);
echo "Admin user created successfully\n";
}
}
命令行工具封装
<?php
// migrate.php - 命令行入口
require_once 'vendor/autoload.php';
// 配置
$config = [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => 'password',
'charset' => 'utf8mb4',
'migrations_path' => __DIR__ . '/migrations',
'seeds_path' => __DIR__ . '/seeds'
];
// 参数处理
$command = $argv[1] ?? 'help';
$pdo = new PDO(
"{$config['driver']}:host={$config['host']};dbname={$config['database']};charset={$config['charset']}",
$config['username'],
$config['password']
);
$migration = new SimpleMigration($pdo, $config['migrations_path']);
switch ($command) {
case 'migrate':
$migration->migrate();
break;
case 'rollback':
$migration->rollback();
break;
case 'seed':
// 运行种子数据
$seederFile = $config['seeds_path'] . '/' . ($argv[2] ?? '') . '.php';
if (file_exists($seederFile)) {
require_once $seederFile;
$seeder = new AdminUserSeeder($pdo);
$seeder->run();
}
break;
default:
echo "Usage: php migrate.php [migrate|rollback|seed]\n";
}
最佳实践建议
版本控制
// 迁移文件命名规范
// YYYYMMDDHHMMSS_description.php
// 20231201120000_create_users_table.php
// 确保按时间顺序执行
function sortMigrations($files) {
sort($files);
return $files;
}
事务处理
// 使用事务确保数据一致性
public function migrate()
{
try {
$this->pdo->beginTransaction();
// 执行迁移操作
$this->pdo->exec("ALTER TABLE users ADD COLUMN phone VARCHAR(20)");
$this->pdo->commit();
} catch (Exception $e) {
$this->pdo->rollBack();
throw $e;
}
}
安全考虑
// 验证迁移文件来源
function validateMigration($file) {
// 检查文件格式
if (!preg_match('/^\d{14}_.+\.php$/', basename($file))) {
throw new Exception("Invalid migration filename: " . $file);
}
// 检查文件是否可读
if (!is_readable($file)) {
throw new Exception("Migration file not readable: " . $file);
}
}
推荐使用 Phinx 或 Laravel Migration 等成熟方案,它们提供了更完善的功能和更好的安全性,如果项目较小或需要自定义迁移逻辑,可以参考上述轻量级实现方案,记住始终在开发环境中测试迁移,并在生产环境中做好备份。