数据库迁移doctrine/migration 2.x的使用方法
前言
数据库迁移在系统升级中是特别必要的,因为你不可能每发一个版本都记录一些sql命令保存在某上地方,然后别人接手手你再告诉它怎么升级当前数据库。自己写一个工具又太麻烦,还好有现成的可以用,如下
官方文档
使用方法 https://www.doctrine-project.org/projects/doctrine-migrations/en/2.2/reference/introduction.html#introduction
API查询 https://www.doctrine-project.org/api/dbal/2.9/Doctrine/DBAL/Schema.html 这个文档并不是迁移库的文档,但是方法类似,可以参考,实在找不到的只能到源码里找啦,截止此文章发布这个迁移库没有详细的api文档
下面是使用示例,并不完善,更多的使用说明可以查文档
安装/配置迁移工具
安装数据库迁移工具,我安装的时候是2.x最新版本
composer require doctrine/migrations
使用,按照官方的使用方法需要在根目录下创建两个配置文件 migrations.php migrations-db.php ,但是我们一般是要集成到项目里面使用的,这里写一个集成的方法,首先创建一个迁移的命令入口文件来 ank.php 替换官方的命令,如下
#!/usr/bin/env php
<?php
//---------------------------------------------------------------
// Setup Global Error Levels
//
//--------------------------------------------------------------
error_reporting(E_ALL);
ini_set('display_errors', 1);
global $loader;
//------------------------------------------------------------------------------
// Load the composer autoloader
//
//------------------------------------------------------------------------------
if (is_dir($vendor = __DIR__ . '/../vendor')) {
$loader = require $vendor . '/autoload.php';
} elseif (is_dir($vendor = __DIR__ . '/../../../vendor')) {
$loader = require $vendor . '/autoload.php';
} else {
die(
'You must set up the project dependencies, run the following commands:' . PHP_EOL .
'curl -s http://getcomposer.org/installer | php' . PHP_EOL .
'php composer.phar install' . PHP_EOL
);
}
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\Migrations\Configuration\Configuration;
use Doctrine\Migrations\Tools\Console\Command;
use Doctrine\Migrations\Tools\Console\Helper\ConfigurationHelper;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
//////////////////////////////////////////这里替换成自己项目中取配置的方法
$app = \ank\App::getInstance();
$config = $app->config('db_config');
$migrationsConfig = $app->config('migrations') ?: [];
$migrations = [
'name' => 'Doctrine Migrations',
'name_space' => 'db\migrations',
'table_name' => 'kl_migration',
'path' => dirname($app->getRuntimePath()) . '/db/migrations',
];
$migrations = array_merge($migrations, $migrationsConfig);
/////////////////////////////////////////////////////////////////////////
$dbParams = [
'driver' => 'pdo_mysql',
'host' => $config['server'],
'port' => $config['port'],
'user' => $config['username'],
'password' => $config['password'],
'dbname' => $config['database_name'],
];
$connection = DriverManager::getConnection($dbParams);
// 迁移组件配置
$configuration = new Configuration($connection);
$configuration->setName($migrations['name']);
$configuration->setMigrationsNamespace($migrations['name_space']);
$configuration->setMigrationsTableName($migrations['table_name']);
$configuration->setMigrationsDirectory($migrations['path']);
$helperSet = new HelperSet();
$helperSet->set(new QuestionHelper(), 'question');
$helperSet->set(new ConnectionHelper($connection), 'db');
$helperSet->set(new ConfigurationHelper($connection, $configuration));
$cli = new Application('Doctrine Migrations');
$cli->setCatchExceptions(true);
$cli->setHelperSet($helperSet);
$cli->addCommands([
new Command\DumpSchemaCommand(),
new Command\ExecuteCommand(),
new Command\GenerateCommand(),
new Command\LatestCommand(),
new Command\MigrateCommand(),
new Command\RollupCommand(),
new Command\StatusCommand(),
new Command\VersionCommand(),
]);
$cli->run();有啦这个文件后就可以使用如下命令来执行自己的迁移啦。如下
# 生成迁移脚本 php ank.php migrations:generate # 执行迁移到最新版本 php ank.php migrations:migrate # --dry-run是空转参数,只显示操作结果,不执行修改 php ank.php migrations:migrate --dry-run # 不执行操作,只写入文件,对于生产环境需要手动验证并执行的场景有用 php ank.php migrations:migrate --write-sql=file.sql # 查看详细信息 php ank.php status # 迁移到指定版本 php ank.php migrations:migrate 20180608161758
注意里面配置的替换,换成自己项目的配置
使用方法
生成一个迁移脚本
php ank.php generate
执行后会在你配置的目录(最上面命令脚本里有个配置项)里生成一个php脚本文件如下,名字不要改

里面有几个方法up是迁移的时候会调用,down是回滚的时候会调用,在这个文件里面可以执行sql等命令来修改数据库,更多的方法可以参考文档,下面只列出来几个常用的方法
添加一个字段
先判断有没有字段,没有的话再添加
$table = $schema->getTable('kl_picture');
if (!$table->hasColumn('domain')) {
$this->addSql("ALTER TABLE `kl_picture` ADD COLUMN `domain` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '资源域名' AFTER `auto_update_time`;");
$this->addSql('UPDATE `kl_picture` SET `domain` = :domain', ['domain' => 'res.zhaokeli.com']);
}添加/删除数据表
判断如果有的话就删除,
if ($schema->hasTable('test1')) {
$schema->dropTable('test1');
}
//也可以直接使用addSql方法创建
$table = $schema->createTable('test1');
$table->addColumn('id', 'integer')->setUnsigned(true)->setAutoincrement(true);
$table->addColumn('name', 'string')->setDefault('')->setLength(20);
$table->setPrimaryKey(['id']);使用connection对象
查询数据
使用数据库连接来执行查询,语法和addSql的参数类似,返回结果为一个数组,更多方法如下 https://www.doctrine-project.org/api/dbal/2.9/Doctrine/DBAL/Connection.html
$data = $this->connection->fetchAllAssociative(string $sql, array $params = [], int[]|string[] $types = [])
增删除改数据
$num = $this->connection->executeUpdate(string $query, array $params = [], array $types = [])
执行一个 INSERT/UPDATE/DELETE 查询并返回受影响的行数,可以使用第二个参数进行绑定,上面的addSql就是调用的这个方法,只不过addSql没有返回值,另外直接执行executeUpdate直接执行没有使用事务,addSql默认开启事务,可以重写下面方法修改是否使用事务
public function isTransactional() : bool
{
return false;
}中断迁移
$this->abortIf(true, 'Something went wrong. Aborting.');
跳过本次迁移
$this->skipIf(true, 'Skipping this migration.');
事务相关
默认情况下事务是开启的,但是在一些情况下我们要直接执行,如需要在表中先添加一个字段,然后下一条语句需要使用这个字段进行一些操作,因为使用啦事务,添加字段这个操作并没有立即执行,这个时候如果下一个查询语句中使用这个字段就会报错字段不存在,
先关闭事务
public function isTransactional(): bool
{
return false;
}执行操作
//添加新字段
$table = $schema->getTable($this->replaceSql('__PREFIX__menu'));
if (!$table->hasColumn('is_dir')) {
$sql = $this->replaceSql('ALTER TABLE `__PREFIX__menu` ADD COLUMN `is_dir` tinyint(1) NOT NULL DEFAULT 0 COMMENT \'是否目录\'');
try {
$this->addSql($sql);
//执行一些其它操作
} catch (\Throwable $e) {
$this->abortIf(true, '添加字段is_dir失败');
}
}里面涉及到数据库的相关操作可以参考这里:https://github.com/doctrine/dbal