数据库迁移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