yii2使用Migrations爲整個數據庫表建立遷移

本教程爲整個數據庫表進行建立遷移,彌補之前未作的工做,且僅適合於Migrations(2.0.8)版本用戶及以上。php

你們都知道Migrations是一個在開發和維護數據庫驅動的應用過程當中,數據庫的結構與源代碼的開發同步更新。例如,在應用開發的過程當中,新建了一張表,在應用部署到生產環境後,發現須要爲這張表建立一個索引以提高查詢性能,等等。由於數據庫結構改變後須要源代碼隨之而改變,Yii支持此類數據庫遷移特徵,這樣你就能夠用數據庫遷移的形式追蹤數據庫的變化,也就是與源代碼同步的版本控制。mysql

那麼我如今數據表有接近300多張,因此不可能每張表進行命令建立遷移,這樣太浪費時間且項目也不止一個,因此我想到一個思路,就是使用命令讓程序批量將每張表建立遷移文件,那麼原生的Migrations據我瞭解是沒辦法實現將表裏每一個字段都輸出到遷移代碼裏面,因此咱們須要稍微改動一下。web

咱們先找到一個核心文件:/vendor/yiisoft/yii2/console/controllers/BaseMigrateController.php
建立遷移的視圖文件:/vendor/yiisoft/yii2/views/createTableMigration.phpsql

咱們先打開核心文件(BaseMigrateController.php)方法:actionCreate 行數大概在:493行。數據庫

使用Migrations命令建立遷移的時候,命令會詢問咱們是否須要建立,填寫y 或 n,那麼咱們既然要批量建立,確定是不能容許這種阻止程序的事情發生,在502行,有個if判斷$this->confirm("Create new migration '$file'?"),這句代碼就是在咱們操做Migrations不管建立或其餘操做的時候都會詢問,那麼咱們在if判斷裏面添加一個或者條件preg_match('/^create_(.+)$/', $name, $matches)意思就是若是我是建立我就不須要通過詢問(固然後期若是有相似需求,能夠直接將這個if判斷詢問幹掉)。windows

在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()yii2

這裏爲何要新增條件 WHERE Key_name<>'PRIMARY',由於當你有個自增主鍵的時候,他也會輸出出來,但這個自增主鍵並非咱們想要的索引字段,因此咱們使用條件將他幹掉。app

下面foreach循環就是爲了等下輸出的時候方便(Non_unique在做者這裏原覺得用Migrations新增索引的時候能該類型,因此就寫上去了,誰知道後面發現索引類型,已經寫死了,必須爲unique類型,createIndex方法代碼在:/vendor/yiisoft/yii2/db/Migration.php 468行)yii

數據表有用到外鍵的朋友,代碼大家可能要本身手寫一小段了,做者項目中未遇到外鍵因此代碼沒寫,在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 endforeach; ?>
]);
<?php if(count($primaryKeyArray) > 0 && !empty($primaryKeyArray)): ?>
  $this->addPrimaryKey('<?= $table ?>', '<?= $table ?>', '<?= implode(",", $primaryKeyArray) ?>');
<?php endif; ?>
<?php if(count($createIndexArray) > 0 && !empty($createIndexArray)): ?>
  <?php foreach($createIndexArray as $key => $value): ?>
    $this->createIndex('<?= $value['Key_name'] ?>', '<?= $table ?>', '<?= $value['Column_name'] ?>', true);
  <?php endforeach; ?>
<?php endif; ?>

以上代碼就是將剛剛查到的數據字段進行循環,而後拼接成字段名 => 字段自增->字段類型(字段大小)->是否爲空->字段默認值->字段註釋(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關鍵詞
代碼:

<?php
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文件了
代碼:

<?php
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
 
 
Options : History : Feedback : Donate Close
相關文章
相關標籤/搜索