linux yii 数据库迁移,yii2使用Migrations为整个数据库表创建迁移

本教程为整个数据库表进行创建迁移,弥补以前未做的工作,且仅适合于Migrations(2.0.8)版本用户及以上。

大家都知道Migrations是一个在开发和维护数据库驱动的应用过程中,数据库的结构与源代码的开发同步更新。例如,在应用开发的过程中,新建了一张表,在应用部署到生产环境后,发现需要为这张表创建一个索引以提升查询性能,等等。因为数据库结构改变后需要源代码随之而改变,Yii支持此类数据库迁移特征,这样你就可以用数据库迁移的形式追踪数据库的变化,也就是与源代码同步的版本控制。

那么我现在数据表有接近300多张,所以不可能每张表进行命令创建迁移,这样太浪费时间且项目也不止一个,所以我想到一个思路,就是使用命令让程序批量将每张表创建迁移文件,那么原生的Migrations据我了解是没办法实现将表里每个字段都输出到迁移代码里面,所以我们需要稍微改动一下。

我们先找到一个核心文件:/vendor/yiisoft/yii2/console/controllers/BaseMigrateController.php

创建迁移的视图文件:/vendor/yiisoft/yii2/views/createTableMigration.php

我们先打开核心文件(BaseMigrateController.php)方法:actionCreate 行数大概在:493行。

使用Migrations命令创建迁移的时候,命令会询问我们是否需要创建,填写y 或 n,那么我们既然要批量创建,肯定是不能允许这种阻止程序的事情发生,在502行,有个if判断$this->confirm("Create new migration '$file'?"),这句代码就是在我们操作Migrations无论创建或其他操作的时候都会询问,那么我们在if判断里面添加一个或者条件preg_match('/^create_(.+)$/', $name, $matches)意思就是如果我是创建我就不需要经过询问(当然后期如果有类似需求,可以直接将这个if判断询问干掉)。

在if判断里面有做了六件事,我们这次仅针对于创建的时候修改,找到else if的preg_match('/^create_(.+)$/', $name, $matches)这个条件里面,以下是我的代码:

$this->addDefaultPrimaryKey();

$primaryKeyArray = $createIndexArray = array();

$tableInfo = Yii::$app->getDb()->getSchema()->getTableSchema($matches[1]);

foreach($tableInfo->columns as $key => $value):

if($value->isPrimaryKey == 1 && !$value->autoIncrement):

$primaryKeyArray[] = $value->name;

endif;

endforeach;

$fieldsIndex = Yii::$app->db->createCommand("SHOW index FROM {$matches[1]} WHERE Key_name<>'PRIMARY'")->queryAll();

foreach($fieldsIndex as $key => $value):

$createIndexArray[$key]['Key_name'] = $value['Key_name'];

$createIndexArray[$key]['Column_name'] = $value['Column_name'];

$createIndexArray[$key]['Index_cat'] = $value['Non_unique'] < 1 ? 'Unique' : 'Normal';

$createIndexArray[$key]['Index_type'] = $value['Index_type'];

endforeach;

$content = $this->renderFile(Yii::getAlias($this->generatorTemplateFiles['create_table']), [

'className' => $className,

'table' => mb_strtolower($matches[1], Yii::$app->charset),

'fields' => $this->fields,

'tableInfo' => $tableInfo,

'primaryKeyArray' => $primaryKeyArray,

'createIndexArray' => $createIndexArray

]);

思路是,先用Yii::$app->getDb()->getSchema()->getTableSchema(表名)方法获取到表字段数据,然后我们循环字段,判断isPrimaryKey是否为1 且 autoIncrement是否不存在(因为有的表可能不需要自增而需要主键,这个循环判断就是为了干这件事),然后我们会发现Yii::$app->getDb()->getSchema()->getTableSchema(表名)方法并不能获取到我的索引字段,那么我们就不要局限于Schema,我们改用mysql语句来查询:Yii::$app->db->createCommand("SHOW index FROM {$matches[1]} WHERE Key_name<>'PRIMARY'")->queryAll()。

这里为什么要新增条件 WHERE Key_name<>'PRIMARY',因为当你有个自增主键的时候,他也会输出出来,但这个自增主键并不是我们想要的索引字段,所以我们使用条件将他干掉。

下面foreach循环就是为了等下输出的时候方便(Non_unique在作者这里原以为用Migrations新增索引的时候能该类型,所以就写上去了,谁知道后面发现索引类型,已经写死了,必须为unique类型,createIndex方法代码在:/vendor/yiisoft/yii2/db/Migration.php 468行)

数据表有用到外键的朋友,代码你们可能要自己手写一小段了,作者项目中未遇到外键所以代码没写,在Yii::$app->getDb()->getSchema()->getTableSchema(表名)方法中,已经查出了表的外键,你们可以利用。

接着往下代码就是渲染视图模板,模板路径在上面刚刚已经说了,这个时候,我们把刚刚查出来的三个数组传进去。

现在开始到视图模板(/vendor/yiisoft/yii2/views/createTableMigration.php):我们修改up方法里面的代码,这里能看到只有一个自增ID。

$this->createTable('= $table ?>', [

<?php foreach ($tableInfo->columns as $key => $value): ?>

'= $key ?>' => $this-><?php if($value->isPrimaryKey == 1 && $value->autoIncrement == 1): ?>primaryKey()<?php else: ?><?php if($value->type == 'smallint'): ?>smallinteger<?php elseif($value->type == 'bigint'): ?>biginteger<?php else: ?>= $value->type ?><?php endif; ?>(<?php if(isset($value->scale)): ?>= $value->size ?>, = $value->scale ?><?php else: ?>= $value->size ?><?php endif; ?>)<?php if(!$value->allowNull): ?>->notNull()<?php endif; ?><?php if(isset($value->defaultValue) && $value->defaultValue != 'CURRENT_TIMESTAMP' && $value->defaultValue != '' && $value->defaultValue != '\'\''): ?>->defaultValue('= $value->defaultValue ?>')<?php endif; ?><?php if(isset($value->comment) && $value->comment != ''): ?>->comment('= $value->comment ?>')<?php endif; ?><?php endif; ?>= ",\n" ?>

]);

<?php if(count($primaryKeyArray) > 0 && !empty($primaryKeyArray)): ?>

$this->addPrimaryKey('= $table ?>', '= $table ?>', '= implode(",", $primaryKeyArray) ?>');

<?php if(count($createIndexArray) > 0 && !empty($createIndexArray)): ?>

<?php foreach($createIndexArray as $key => $value): ?>

$this->createIndex('= $value['Key_name'] ?>', '= $table ?>', '= $value['Column_name'] ?>', true);

以上代码就是将刚刚查到的数据字段进行循环,然后拼接成字段名 => 字段自增->字段类型(字段大小)->是否为空->字段默认值->字段注释(Migrations2.0.8版本才支持注释2.0.8版本以下不支持字段注释)。

好,上面的代码我能满足百分之80以上的字段,除了一些个别特殊的字段,什么是特殊的字段呢?例如,在mysql类型中是:smallint 但我在Migrations中必须是 smallinteger 包括 bigint 也要改为 biginteger,目前我就发现这两个不一样,其他的暂时还没遇到。

然后我们开始输出主键字段(并不是自增的哦~自增的如果存在就已经在上面输出了,这里的代码只处理主键字段)我们先判断数组是否存在且数组个数大于0,这里不能使用foreach来循环主键数组,因为$this->addPrimaryKey('name', 'tableName', 'columns')方法只能存在一个,所以我们使用PHP的 implode()方法进行拆分数组。

主键的解决了,还差一个新增索引的,新增索引方法为 $this->createIndex('name', 'tableName', 'Column_name'),这个方法允许存在多个,那么我们就先判断数组是否存在且个数是否大于0,然后再使用 foreach 方法,Key_name是新增索引时的名字,table 就是你新增索引到哪个表,Column_name 就是字段名。

以上步骤都完成以后,我们就开始新建console命令啦~

作者创建的控制器是:TimerController.php,如果你们有控制器可以直接使用,再新建一个Model文件,并且将引入Model关键词

代码:

namespace console\controllers;

use Yii;

use yii\console\Controller;

use console\models\MigrationDb;

/**

* 定时任务

* @author mo

*

*/

class TimerController extends Controller

{

public function actionMigrationdb()

{

$Migrate = new MigrationDb();

// 获取迁移目录路径 console/migrations/

$dirName = Yii::getAlias('@console').'/migrations';

// 先删除该路径下已生成的所有文件

$Migrate->deleteFile($dirName);

// 获取所有表名 开始循环获取表字段信息,创建迁移

$db = Yii::$app->getDb();

$tablesName = $db->getSchema()->getTableNames();

foreach($tablesName as $key => $value)

{

$tablesInfo = $db->getSchema()->getTableSchema($value);

exec("yii migrate/create create_".$value, $info);

}

}

}

我们先实例化模型文件,然后获取到存放迁移文件的路径,先将迁移路径下的所有迁移文件删除掉(避免重复),然后我们就使用:Yii::$app->getDb()->getSchema()->getTableNames()获取所有的表名,接着就 foreach 循环所有的表,key为键值 value为表名,然后我们使用php的 exec 函数执行命令,这命令的意思是,创建迁移文件,文件名是以:create_表名 形式拼接好的,$info 可以输出打印调试结果,执行成功将会返回 New migration created successfully.。

好了我们最后开始写Model文件了

代码:

namespace console\models;

use yii\base\Model;

class MigrationDb extends Model

{

/**

*删除该目录下的所有文件及文件夹

*@dirName 路径名

*/

public static function deleteFile($dirName)

{

if($handle = opendir($dirName))

{

while(false != ($item = readdir($handle)))

{

if($item != '.' && $item != '..')

{

if(is_dir($dirName."/".$item))

{

self::deleteFile($dirName."/".$item);

}else

{

unlink($dirName.'/'.$item);

}

}

}

closedir($handle);

}

}

}

这里就是找到指定目录将其目录下的所有文件及文件夹删除掉(如果不满足你们需求可以进行更改)。

到了最后紧张又刺激的时刻了,我们的工作已经完成,就差运行命令调试。

我们先将所有表备份一份并导出到本地(以防万一,我不舍得你们跑路啊),确保所有表都在的时候,我们就是用命令执行console任务。

(先进入到你的程序根目录,有yii.bat的那里)

windows的DOC命令:/你的文件夹路径/yii timer(控制器名)/migrationdb(方法名)。

Linux命令:老子不会。

这个时候:console/migrations/ 目录下会创建迁移文件,成功创建完迁移文件之后,我们将所有表删除掉(删除之前记得备份!备份!!备份!!!),然后我们打开命令执行:yii migrate,这个时候有多少个迁移文件会告诉你,还会问你是否执行,我们输入y 确定执行,这个时候就开始往数据库导入表了,如有报错可发截图并询问我或者百度。

如果报表已存在的错误的话,那么就是你没有将表删完,Migrations创建迁移 跟 其他操作的时候,会自动新增一张为 migrtions的表,这张表是记录的。

G

M

T

Detect languageAfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu

AfrikaansAlbanianArabicArmenianAzerbaijaniBasqueBelarusianBengaliBosnianBulgarianCatalanCebuanoChichewaChinese (Simplified)Chinese (Traditional)CroatianCzechDanishDutchEnglishEsperantoEstonianFilipinoFinnishFrenchGalicianGeorgianGermanGreekGujaratiHaitian CreoleHausaHebrewHindiHmongHungarianIcelandicIgboIndonesianIrishItalianJapaneseJavaneseKannadaKazakhKhmerKoreanLaoLatinLatvianLithuanianMacedonianMalagasyMalayMalayalamMalteseMaoriMarathiMongolianMyanmar (Burmese)NepaliNorwegianPersianPolishPortuguesePunjabiRomanianRussianSerbianSesothoSinhalaSlovakSlovenianSomaliSpanishSundaneseSwahiliSwedishTajikTamilTeluguThaiTurkishUkrainianUrduUzbekVietnameseWelshYiddishYorubaZulu

Text-to-speech function is limited to 200 characters

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值