yii2 uses Migrations to create migrations for the entire database table

This tutorial creates migrations for the entire database table, making up for the work that has not been done before, and is only suitable for users of Migrations (2.0.8) version and above.

We all know that Migrations is a process of developing and maintaining database-driven applications, and the structure of the database is updated synchronously with the development of the source code. For example, in the process of application development, a new table is created, and after the application is deployed to the production environment, it is found that an index needs to be created for this table to improve query performance, and so on. Because the source code changes after the database structure changes, Yii supports this kind of database migration feature, so that you can track database changes in the form of database migrations, that is, version control synchronized with the source code.

Then I now have nearly 300 data tables, so it is impossible to create and migrate each table by command, which is too time-consuming and there are more than one project, so I came up with an idea, which is to use the command to let the program create and migrate each table in batches file, then the native Migrations, as far as I know, cannot output every field in the table to the migration code, so we need to change it slightly.

We first find a core file: /vendor/yiisoft/yii2/console/controllers/BaseMigrateController.php
Create a migrated view file: /vendor/yiisoft/yii2/views/createTableMigration.php

We first open the core file (BaseMigrateController.php) method: actionCreatethe number of lines is about: 493 lines.

When using the Migrations command to create a migration, the command will ask us if we need to create, fill in y or n, then since we want to create batches, we must not allow this kind of blocker to happen. In line 502, there is an if judgment $this->confirm("Create new migration '$file'?"), this The sentence code is to ask when we operate Migrations whether it is created or other operations, then we add an or condition to the if judgment, which preg_match('/^create_(.+)$/', $name, $matches)means that if I am creating, I don’t need to ask (of course, if there is a similar requirement later, you can directly add This if judgment asks to kill).

There are six things done in the if judgment. This time we only modify it at the time of creation. Find preg_match('/^create_(.+)$/', $name, $matches)the condition of else if. The following is my code:

$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
]);

The idea is to first use the Yii::$app->getDb()->getSchema()->getTableSchema(表名)method to obtain the table field data, and then we loop the fields to determine whether isPrimaryKey is 1 and whether autoIncrement does not exist (because some tables may not need to auto-increment and need a primary key, this loop judgment is to do this thing ), and then we will find that Yii::$app->getDb()->getSchema()->getTableSchema(表名)the method cannot get my index field, then we will not be limited to Schema, we will use the mysql statement to query: Yii::$app->db->createCommand("SHOW index FROM {$matches[1]} WHERE Key_name<>'PRIMARY'")->queryAll().

Why add a new condition here WHERE Key_name<>'PRIMARY', because when you have an auto-incrementing primary key, it will also be output, but this auto-incrementing primary key is not the index field we want, so we use the condition to kill it.

The following foreach loop is for the convenience of waiting for the output (Non_unique was originally thought by the author to be of this type when adding an index with Migrations, so I wrote it down. Who knows that the index type is found later, it has been written dead, and it must be unique. Type, createIndex method code in: /vendor/yiisoft/yii2/db/Migration.php line 468)

Friends who use foreign keys in the data table, you may have to write a small piece of code by yourself. The author did not encounter foreign keys in the project, so the code was not written. In the Yii::$app->getDb()->getSchema()->getTableSchema(表名)method, the foreign key of the table has been found, you can use it.

The next code is to render the view template. The template path has just been mentioned above. At this time, we pass in the three arrays we just checked.

Now go to the view template (/vendor/yiisoft/yii2/views/createTableMigration.php): we modify upthe code in the method, and here we can see that there is only one auto-incrementing 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; ?>

The above code is to loop the data fields just found, and then concatenate them into 字段名=> 字段自增->字段类型(字段大小)->是否为空->字段默认值->字段注释(version 2.0.8 only supports annotations, and versions below 2.0.8 do not support field annotations).

Well, the above code can satisfy more than 80% of the fields, except for some individual special fields, what are the special fields? For example, in the mysql type it is: but I must change it to includesmallint in Migrations . At present, I have found that these two are different, and the others have not been encountered yet.smallintegerbigintbiginteger

Then we start to output the primary key field (it is not self-increment~ If the self-increment exists, it will already be output above. The code here only deals with the primary key field). We first determine whether the array exists and the number of the array is greater than 0, which cannot be used here. foreachTo loop the primary key array, because $this->addPrimaryKey('name', 'tableName', 'columns')there can only be one method, we use the PHP implode()method to split the array.

The primary key is solved, but there is still one more index to add. The new index method is $this->createIndex('name', 'tableName', 'Column_name'), this method allows multiple, then we first judge whether the array exists and whether the number is greater than 0, and then use the foreachmethod , which Key_nameis to add an index The name of the time tableis the table you add the index to, which Column_nameis the field name.

After the above steps are completed, we will start to create a new console command.
The controller created by the author is: TimerController.php. If you have a controller that can be used directly, create a new Model file and introduce the Model keyword
Code:

<?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);
        }
    }
}

We first instantiate the model file, then get the path to store the migration file, first delete all the migration files under the migration path (to avoid duplication), and then we use: Yii::$app->getDb()->getSchema()->getTableNames()get all the table names, then foreachloop through all the tables, The keykey value valueis the table name, and then we use the php execfunction to execute the command. This command means to create a migration file. The file name is create_表名spliced ​​in the form of: $infoprint and debug results can be output, and it will be returned if the execution is successful New migration created successfully..

Well, we finally started to write the Model file
Code:

<?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);
        }
    }
}

Here is to find the specified directory and delete all files and folders in its directory (if it does not meet your needs, you can make changes).

At the last tense and exciting moment, our work has been completed, and we need to run the command to debug.

Let's back up all the tables and export them locally (just in case, I don't want you to run away), and when all the tables are there, we just use the command to execute the console task.

(Go to the root directory of your program first, where there is yii.bat)

Windows DOC command: /your folder path/yii timer (controller name)/migrationdb (method name).
Linux command: I won't.

At this time: a migration file will be created in the console/migrations/ directory. After the migration file is successfully created, we will delete all tables (remember to backup before deleting! Backup!! Backup!!!), and then we open the command to execute: yii migrate , at this time, it will tell you how many migration files are there, and it will ask you whether to execute it. We enter yOK to execute it. At this time, the table will be imported into the database. If there is an error, you can send a screenshot and ask me or Baidu.

If there is an error in the report, then you have not deleted the table. When Migrations creates migrations and other operations, it will automatically add a table for migrations, which is recorded.

 
 
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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325256232&siteId=291194637