数据库迁移doctrine/migration的使用方法

来源:赵克立博客 分类: PHP 标签:数据库发布时间:2020-01-14 09:50:18最后更新:2020-10-21 22:47:39浏览:366
版权声明:
本文为博主原创文章,转载请声明原文链接...谢谢。o_0。
更新时间:
2020-10-21 22:47:39
温馨提示:
技术类文章有它的时效性,请留意文章更新时间,如发现内容有误请留言指出,防止别人"踩坑",我会及时更新文章

前言

数据库迁移在系统升级中是特别必要的,因为你不可能每发一个版本都记录一些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脚本文件如下,名字不要改

2001141578980723903898.png

里面有几个方法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失败');
    }
}



微信号:kelicom QQ群:215861553 紧急求助须知
点击更换验证码
Win32/PHP/JS/Android/Python