CakePHP3のマイグレーションまとめ

CakePHP3のマイグレーションについて調べたので備忘録。(色んな記事の寄せ集めという感じ:sunglasses:)

- 2017-05-25時点の情報
- 検証環境
 - CakePHP3 :　Ver. 3.4.6
 - MariaDB :　　Ver. 10.1.23
- 参考ドキュメント
 - https://book.cakephp.org/3.0/ja/migrations.html
 - http://docs.phinx.org/en/latest/migrations.html
 - QiitaのCakePHP3マイグレーション関連記事


# **概要**
- CakePHP3ではマイグレーションツール [Phinx](https://phinx.org/) を採用している (Cakeのコマンドはラッパー)
- コマンドが2種類ある (**bin/cake migrations** と **bin/cake bake migration**)
- **/bin/cake** コマンドは **php /bin/cake.php** でもOK
- マイグレーションファイルは **config/Migration/** 以下に作成される
- ファイル名は、**作成日時(YYYYMMDDHHMMSS)_マイグレーション名.php**
- 明示的にオプション指定しない限り、defaultのデータベース設定を使用する
- migrationコマンドを実行すると、データベースにphinxlogテーブルを作成し、このテーブルで実行履歴を管理する

```
MariaDB > select * from phinxlog;
+----------------+----------------+---------------------+---------------------+------------+
| version        | migration_name | start_time          | end_time            | breakpoint |
+----------------+----------------+---------------------+---------------------+------------+
| 20170525090543 | Initial        | 2017-05-25 09:05:43 | 2017-05-25 09:05:43 |          0 |
| 20170525090709 | CreateProducts | 2017-05-25 09:27:41 | 2017-05-25 09:27:41 |          0 |
+----------------+----------------+---------------------+---------------------+------------+
```


# **マイグレーション適用状況を確認**

```dummy:コマンド
bin/cake migrations status
```

↓結果はこんな感じ

```dummy:結果
 Status  Migration ID    Migration Name 
-----------------------------------------
     up  20170525090543  Initial 
   down  20170525090709  CreateProducts 
```

- up = 実行済み
- down = 未実行


# **現在のDBからマイグレーションファイルを作成**

https://book.cakephp.org/3.0/ja/migrations.html#id14

```dummy:コマンド
bin/cake bake migration_snapshot Initial
```

- config/Migration/YYYYMMDDHHMMSS_Initial.php がファイルが作成される
- config/Migrations/schema-dump-default.lock がファイルが作成(更新)される
- スキーマのみで、データ部分は作成されない (データはseed機能で入れる)
- Initialはマイグレーション名なので他の名前でもOK
- migrations statusは実行済みになる
- 作成したファイルをmigrationコマンドでリストアしても同じ定義には戻らなかった(MySQL/MariaDBの場合)
  - tinyint(x)が、int(x)に変わっている
      - tinyint(1)の場合、恐らくCakePHP内部で、booleanをtinyint(1)として扱っているのでint(x)にならない
      - tinyintを指定したい場合、MySQL用アダプタを使用して書き直す必要あり？
  - インデックス, ユニークキー, 外部キーの名前が少し変わっている


# **マイグレーションファイルを作成**

https://book.cakephp.org/3.0/ja/migrations.html#id3

作成方法は2通り。

**作成方法によって、マイグレーションファイル冒頭のuse宣言が異なる (内部的にはほぼ同じっぽい)**

## **パターン1**　`bin/cake bake migration マイグレーション名` で作成 (お手軽版)

### **シンタックス**

`bin/cake bake migration マイグレーション名 カラム定義`

- マイグレーション名
 - https://book.cakephp.org/3.0/ja/migrations.html#id6
 - マイグレーション名の命名パターンによって作成される処理内容が変わる


| 命名パターン| 処理内容     |
|----|---------|
| (/^(Create)(.*)/)| 指定したテーブルを作成     |
| (/^(Drop)(.*)/) | 指定したテーブルを削除 |
| (/^(Add).*(?:To)(.*)/) | 指定したテーブルにカラム追加 |
| (/^(Remove).*(?:From)(.*)/) | 指定のテーブルのカラムを削除 |
| (/^(Alter)(.*)/) | 指定したテーブルを変更 (CreateTable と AddFieldの別名) |



- カラム定義
 - https://book.cakephp.org/3.0/ja/migrations.html#id8
 - 以下のパターンで指定可能
     - fieldName:fieldType?[length]:indexType:indexName


### **例) テーブル作成**

```dummy:コマンド
bin/cake bake migration CreateProducts name:string description:text created modified
```

- config/Migration/YYYYMMDDHHMMSS_CreateProducts.php が作成される
- change()メソッド内にaddColumn()とcreate()が記述されている

```php
<?php
use Migrations\AbstractMigration;

class CreateProducts extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $table = $this->table('products');
        $table->addColumn('name', 'string', [
            'default' => null,
            'limit' => 255,
            'null' => false,
        ]);
        $table->addColumn('description', 'text', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('created', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->addColumn('modified', 'datetime', [
            'default' => null,
            'null' => false,
        ]);
        $table->create();
    }
}
```


### **例) カラム追加**

```dummy:コマンド
bin/cake bake migration AddPriceToProducts price:decimal
```

- config/Migration/YYYYMMDDHHMMSS_AddPriceToProducts.php が作成される
- change()メソッド内にaddColumn()とupdate()が記述されている

```php
<?php
use Migrations\AbstractMigration;

class AddPriceToProducts extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $table = $this->table('products');
        $table->addColumn('price', 'decimal', [
            'default' => null,
            'null' => false,
        ]);
        $table->update();
    }
}
```


### **例) カラム削除**

```dummy:コマンド
bin/cake bake migration RemovePriceFromProducts price
```

- config/Migration/YYYYMMDDHHMMSS_RemovePriceFromProducts.php が作成される
- change()メソッド内にremoveColumn()とupdate()が記述されている

```php
<?php
use Migrations\AbstractMigration;

class RemovePriceFromProducts extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $table = $this->table('products');
        $table->removeColumn('price');
        $table->update();
    }
}
```


### **例) テーブル削除**

```dummy:コマンド
bin/cake bake migration DropProducts
```

- config/Migration/YYYYMMDDHHMMSS_DropProducts.php が作成される
- change()メソッド内にdrop()が記述されている

```php
<?php
use Migrations\AbstractMigration;

class DropProducts extends AbstractMigration
{
    /**
     * Change Method.
     *
     * More information on this method is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-change-method
     * @return void
     */
    public function change()
    {
        $table = $this->table('products');
        $table->drop();
    }
}
```



## **パターン2**　`bin/cake migrations create マイグレーション名` で作成 (カスタム定義用)

### **例)**

```dummy:コマンド
bin/cake migrations create CreateProducts
```

- config/Migration/YYYYMMDDHHMMSS_create_products.php が作成される
- 何も処理が記述されていないので、1から処理を記述する
- 引数のマイグレーション名はキャメルケースにしないとエラーになる

```php
<?php

use Phinx\Migration\AbstractMigration;

class CreateProducts extends AbstractMigration
{
    /**
     * Change Method.
     *
     * Write your reversible migrations using this method.
     *
     * More information on writing migrations is available here:
     * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
     *
     * The following commands can be used in this method and Phinx will
     * automatically reverse them when rolling back:
     *
     *    createTable
     *    renameTable
     *    addColumn
     *    renameColumn
     *    addIndex
     *    addForeignKey
     *
     * Remember to call "create()" or "update()" and NOT "save()" when working
     * with the Table class.
     */
    public function change()
    {
      // https://book.cakephp.org/3.0/ja/migrations.html や
　　　　　　　　　　　　// http://docs.phinx.org/en/latest/migrations.html を参考に処理を書く
    }
}
```

## マイグレーションファイルのメソッドについて

以下のメソッドが使用可能

- **up()**
 - http://docs.phinx.org/en/latest/migrations.html#the-up-method
 - `migrations migrate`時に実行する処理を書く
- **down()**
 - http://docs.phinx.org/en/latest/migrations.html#the-down-method
 - `migrations rollback`時に実行する処理を書く
- **change()**
 - http://docs.phinx.org/en/latest/migrations.html#the-change-method
 - up()と同様にmigrate時に実行する処理を書いておくと、down()を自動的に推測してくれる
     - 自動的に推測できる以下のコマンドしか書けない
         - createTable
         - renameTable
         - addColumn
         - renameColumn
         - addIndex
         - addForeignKey
     - dropTableやremoveColumn等は、戻すべき元の定義が推測できないのでNG。up()/down()に書く
 - change()だけで、`migrations migrate` と `migrations rollback`両方いける
 - change()内ではcreate()かupdate()で追加・更新を行う。save()は使えない。drop()は使えるみたい

　
**※ change()指定時は、up()やdown()を書いても無視される**
**※ なので、change()のみを書いたファイルか、up()/down()を書いたファイルを作成する**


## Phinxで利用可能なフィールドの型

https://book.cakephp.org/3.0/ja/migrations.html#id8

- string
- text
- integer
- biginteger
- float
- decimal
- datetime
- timestamp
- time
- date
- binary
- boolean
- uuid


**RDBMS固有の型、MySQLのtinyint等を指定したい場合、以下のアダプタを使用する**

- MySQL用
 - http://docs.phinx.org/en/latest/migrations.html#limit-option-and-mysql
- PostgreSQL用
 - http://docs.phinx.org/en/latest/migrations.html#limit-option-and-postgresql



# **マイグレーション実行**

https://book.cakephp.org/3.0/ja/migrations.html#migrate

## 最新まで実行

```dummy:コマンド
bin/cake migrations migrate
```

## 指定したマイグレーションIDまで実行

```dummy:コマンド
bin/cake migrations migrate -t マイグレーションID
```

# **ロールバック**

https://book.cakephp.org/3.0/ja/migrations.html#rollback

## 1つ前に戻す

```dummy:コマンド
bin/cake migrations rollback
```

## 指定したマイグレーションIDまで戻す

```dummy:コマンド
bin/cake migrations rollback -t マイグレーションID
```


# **マイグレーションを実行済みにする(DBには反映しない)**

https://book.cakephp.org/3.0/ja/migrations.html#mark-migrated


## すべて実行済みにする

```dummy:コマンド
bin/cake migrations mark_migrated
```

## 指定したマイグレーションIDだけマイグレーション済みにする

```dummy:コマンド
bin/cake migrations mark_migrated --target=マイグレーションID --only
```

直接DBの方を変更済みの時などで、マイグレーションを実行したくない場合に、実行済みに出来る。


### 実行前

```dummy:migrations&nbsp;status
 Status  Migration ID    Migration Name 
-----------------------------------------
     up  20170525090543  Initial 
   down  20170525090709  CreateProducts 
```

```dummy:phinxlogテーブル
MariaDB > select * from phinxlog;
+----------------+----------------+---------------------+---------------------+------------+
| version        | migration_name | start_time          | end_time            | breakpoint |
+----------------+----------------+---------------------+---------------------+------------+
| 20170525090543 | Initial        | 2017-05-25 09:05:43 | 2017-05-25 09:05:43 |          0 |
+----------------+----------------+---------------------+---------------------+------------+
```

という状況で、コマンドを実行すると

```dummy:コマンド
bin/cake migrations mark_migrated
```

以下のようになる (productsテーブルは作成されていない)

### 実行後

```dummy:migrations&nbsp;status
 Status  Migration ID    Migration Name 
-----------------------------------------
     up  20170525090543  Initial        
     up  20170525090709  CreateProducts 
```

```dummy:phinxlogテーブル
MariaDB > select * from phinxlog;
+----------------+----------------+---------------------+---------------------+------------+
| version        | migration_name | start_time          | end_time            | breakpoint |
+----------------+----------------+---------------------+---------------------+------------+
| 20170525090543 | Initial        | 2017-05-25 09:05:43 | 2017-05-25 09:05:43 |          0 |
| 20170525090709 | CreateProducts | 2017-05-25 09:27:41 | 2017-05-25 09:27:41 |          0 |
+----------------+----------------+---------------------+---------------------+------------+
```


# **差分算出用ダンプファイルのみ作成**

https://book.cakephp.org/3.0/ja/migrations.html#dump-bake

```dummy:コマンド
bin/cake migrations dump
```

- config/Migrations/schema-dump-default.lock がファイルが作成(更新)される
- その時点のDB情報が保存されている
- ダンプファイルは、migrate / rollback / スナップショット実行時にも作成される
- **このコマンドで作成したダンプファイルでは、うまく差分算出できなかった**


# **現在のDB状態とダンプファイルの差分からマイグレーションファイルを作成**

https://book.cakephp.org/3.0/ja/migrations.html#id15

```dummy:コマンド
bin/cake bake migration_diff NameOfTheMigrations
```

- config/Migration/YYYYMMDDHHMMSS_NameOfTheMigrations.php が作成される
- 前回のダンプファイル(schema-dump-default.lock)更新以降に追加・削除されたスキーマのマイグレーション処理が書かれている
 - **`migrations dump`で作成したダンプファイルでは、差分がうまく出力されなかった(全スキーマのcreate文が出力された)**
 - `migration_snapshot` / `migrations migrate` で作成したダンプファイルでは問題なく差分のみ出力された
- config/Migrations/schema-dump-default.lock がファイルが作成(更新)される



# **初期データの作成**

https://book.cakephp.org/3.0/ja/migrations.html#seed

Phinxのseed機能により初期データ用ファイルを作成できる。
あとで追記予定。