Phinx導入ガイド

# 導入のための留意点
筆者は、CakePHP3での採用と他のフレームワークでも非依存で利用できるからという理由で導入をした。

しかしCakeDCのmigrations pluginと同じ考え方なんだろうという先入観があったために、かえって理解しづらい点があったので、そういう方は次の点を念頭に置くと悩みが少ない（かもしれない）。

- 既にあるDBをリバースエンジニアリングする機能はない。
     - CakePHPのschema.phpに相当する概念がない。
- DDLをSQLではなく、phpのコードで書けるのが利点…と捉えた方がいい。
	- マイグレーションをPhinxの管理下に置くということは、何もない状況からマイグレーションできるようにすべきだから、既存のDDLがある場合は書き換える必要がある。
	- SQLを書けるメソッド（executeやquery）があるので、ヒアドキュメントを使う方法はある。
- 既にDBを構成している環境では使いにくい。
	- Phinxでは専用の履歴テーブル（デフォルトでPhinxlog）を作って、マイグレーションがどこまで適用しているかのステータスを保持する。
	- 既にDB展開済の場合は、Phinxが持つステータス状況と実態が合わずに、問題が起こりうる。
   - それを回避するには、マイグレーションのup/downメソッドにテーブルやフィールド（コラム）の実在確認の処理を書くか、初回を空っぽにして、２回目からまじめに書く。（ただしDDLは別提供）
   - または、初回マイグレーションだけ[ぶっ放し](#sqlの実行)、２回目からまじめに書く。

# 導入 
既にリポジトリに入っており、利用者としてセットアップしたい人は[インストール](#インストール)の項から参照のこと。

## 展開ディレクトリの作成
Composerを使うので、他のライブラリーをComposerでインストールしている場合は、同じvendorsに展開される。

もし独立した環境にしたい場合は、別にディレクトリを作り、その中で展開する。

## gitignoreの設置
お好みでgitignoreを設置する。
composerでインストールできるものはリポジトリ管理に入れず、自分たちが書いたものだけを入れるというセオリーがあるので、全部をコミットするのは推奨しない。

```bash
$ vi .gitignore
```

```
vendor
Phinx.yml
```

## composer

composer.jsonを配置 

```bash
$ vi composer.json
```

``` 
{ 
    "require":{ 
        "robmorgan/Phinx": "*" 
    } 
} 
``` 

もし、チームで、環境にcomposerをインストールしない場合は、composer.pharをプロジェクトに配置してコミットしておく。 

```bash
$ curl -s http://getcomposer.org/installer | php 
```

# インストール
環境にcomposerをインストール済の場合は 

```bash
$ composer install 
```

composerをインストールされていない場合は、composer.pharを使う。

```bash
$ php composer.phar install 
```

Phinxが使用できる状態になったので、環境定義（Phinx.yml）を生成し、DB情報を編集する。 

```bash
$ ./vendor/bin/Phinx init
$ vi Phinx.yml
```

environmentsのところに、production, development, testingの３つの環境定義があるが、デフォルトはdevelopmentなので、複数の環境を使い分けない場合は、developmentのところだけ書き換える。

```
    development:
        adapter: mysql
        host: localhost
        name: （データベース名）
        user: （ログインID）
        pass: （パスワード）
        port: 3306
        charset: utf8
```

# マイグレーション処理の流れ
詳細はそれぞれ後述ので、概要として流れを解説する。

1. マイグレーション定義ファイルを生成する
ファイルを作って、定義が書ける準備をする。

2. マイグレーション定義ファイルに処理を書く。
定義ファイルの中にchange, up, downのメソッドが生成されるので、これに記述を行う。

3. マイグレーションを実行する。
これによりDBに変更が実行され、Phinxlogテーブルが作成され、どこまでマイグレーションされているかを記録する。

以降、変更を加えるごとに１〜３を実行する。gitにおけるプル→コミットのように、３→１→２→３となる。
マイグレーションを反映させるには、３だけを実行する。

留意すべきは、リバースしないのでDBを直接編集せず、定義ファイルに記述し、それを実行することでDBに変更を与えること。

# 1.マイグレーション定義ファイルの生成
まず空の定義ファイルを作り、そこに処理を書いてマイグレーション定義が完成する。

次のコマンドで、定義ファイルが作成される。定義ファイルを格納するフォルダ（デフォルトでは ./migrations）が存在していない場合は、自動的に作成される。

```bash
$ ./vendor/bin/Phinx create (定義名)
```

定義名とは、マイグレーション定義ファイルに付ける名前であり、その中で定義されるクラス名になる。

公式ドキュメントでは「MyNewMigration」という名前になっているが、何をしたのか分かる名前を付ける方がよい。（[statusを参照](#status)）

定義名は、CamelCaseでなければならない。
「initial」はエラーになり、「Initial」はOK。

これで、./migrations/20150614151105_initial.php という日付＋定義名でファイルが作成される。

ちなみに、この時点でPhinx.ymlを設定していなくてもエラーにならないことからも、マイグレーションの実行以外ではDBへのアクセスはない。

# 2.マイグレーションファイルに処理を書く
作成された定義ファイルを開く。

```bash
$ vi ./migrations/20150614151105_initial.php （例）
```

内容としては、change, up, downの３つのメソッドがあり、changeは最初コメントアウトされている（後述の通り、up/downと排他的に動作するため）。

メソッド | 処理
---+---
up | マイグレーションするときに実行される
down | ロールバックするときに実行される
change | マイグレーションするときに実行されるが、ロールバックする場合も、逆処理を行う。なお、このメソッドが有効（宣言されている状態）の場合、up/downは実行されない。

逆処理とは、addColumnの場合はremoveColumnが実行されることを意味する。当然全てが逆処理できるわけではなく、対応しているのは下記の通り。

changeメソッド内のコマンド（migrateで実行） | rollbackで実行されるコマンド
-------------+---------------
createTable | dropTable
renameTable | renameTable（引数逆）
addColumn | removeColumn
renameColumn | renameColumn（引数逆）
addIndex | removeIndex
addForeignKey | dropForeignKey

これ以外の処理を行う場合は、up/downでそれぞれ処理を書く方がよい。

## テーブル操作
```php
$table = $this->table(‘テーブル名’);
```
ここで代入した$tableに対して、処理を行う。

### テーブルの追加
addColumnとコラム追加をしてから、create()メソッドを呼ぶ。
addColumnについては[後述](#列の追加)

```php
$table->addColumn('name', 'string', array('limit' => 100)
      ->addColumn('age', 'integer')
      ->create();
```

### create/updateとsave
createはテーブル生成を明示するものだが、[公式ドキュメント](http://docs.Phinx.org/en/latest/migrations.html?highlight=change#creating-a-table)ではcreateではなく、saveメソッドを呼ぶと書かれている。
saveは、createまたはupdateになるので、基本的にup/downメソッドに記述する場合はsaveでよい。

しかしchangeメソッドで定義する場合は、[公式ドキュメント](http://docs.Phinx.org/en/latest/migrations.html#the-change-method)にも書かれている通り、createまたはupdateを明示しなければならないと書かれている。理由はrollbackするときにコラムを消すのか、droptableするのかが自動的には判断できないため。
そのため、このドキュメントではcreate/updateを使用している。

### テーブルの削除
```php
$this->dropTable('users');
```

## 列操作
### 列の追加
```php
$table->addColumn('列名', '型' , array(オプション))
      ->addColumn（…繰り返し…）
      ->save();
```
#### 列名
フィールド名

#### 型
型には次のものが使用できる。…と言いたいところだが、（お察し下さい）なので、[公式ドキュメント](http://docs.Phinx.org/en/latest/migrations.html#valid-column-types)参照。

なおstringは、limitオプションを付けない限り、デフォルトではvarchar(255)。

#### オプション
（お察し下さい）なので、[公式ドキュメント](http://docs.Phinx.org/en/latest/migrations.html#valid-column-options)参照。

### 列の削除
```php
$table->removeColumn('列名')
      ->update()
```

### 列名の変更
```php
$table->renameColumn('変更前', '変更後');
```

### インデックス
addColumnなどと組み合わせて定義する。

```php
$table->addIndex(array('列名1', '列名n'))
      ->update()
```

## 既にテーブルが存在しているかどうか
```php
$exists = $this->hasTable('users');
if ($exists) {
     〜
}
```

## SQLの実行
executeとqueryがあるが、返値に違いがある。

```php
//影響を及ぼしたレコード数が返値
$count = $this->execute('DELETE FROM users');
//実行した結果が返値
$rows = $this->query('SELECT * FROM users');
```

既に作成済みのDDLをぶっ放す。

```php
    public function up()
    {
        $query = <<<EOQ
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `age` integer,
  `created` datetime default NULL,
  `modified` datetime default NULL,
  PRIMARY KEY  (`id`)
)AUTO_INCREMENT=1 ;
EOQ;
        $this->execute($query);
    }

    public function down()
    {
        $this->dropTable('users');
    }
```

## idの自動生成を抑制する
Phinxは、id列を自動的に追加する。自分でid列の仕様を指定したり、primary keyを変更したい場合は邪魔になる。その場合は、下記のようにテーブル指定時にオプションで指定する。

```
$table = $this->table('statuses',
    [
        'id' => false,
        'primary_key' => ['id']
    ]);
$table->addColumn('id', 'char', ['limit' => 36])
      ->addColumn('name', 'char', ['limit' => 255])
      ->addColumn('model', 'string', ['limit' => 128])
      ->create();
```
[出典](https://github.com/cakephp/migrations#usage-for-custom-primary-key-id-in-tables)



# 3.マイグレーションを実行する
## migrate
マイグレーションの実行。マイグレーション定義ファイルのupメソッドまたはchangeメソッドを実行する。（changeメソッドがある場合は、upメソッドは無視される）
後述の-tオプションなしでは、最新まで一括でマイグレーションする。

```bash
$ ./vendor/bin/Phinx migrate
```

## rollback
１つずつ戻す。downメソッドまたはchangeメソッドの逆処理を実行する。（up同様changeメソッドがある場合はdownメソッドは無視される。）

```bash
$ ./vendor/bin/Phinx rollback 
```

## ターゲットを指定する
ターゲットを指定したい場合は、-tオプションを付け、マイグレーションIDを指定する。

例）20150614154730_my_migration.phpの場合

```bash
$ ./vendor/bin/Phinx migrate -t 20150614154730
```

## status
全てのマイグレーション一覧を表示し、かつ現在のマイグレーション適用状況を表示する。

```bash
$ ./vendor/bin/Phinx status
```

```
 Status  Migration ID    Migration Name
-----------------------------------------
     up  20150614070924  InitialTable
     up  20150615021023  Users
   down  20150615111810  Logs
   down  20150615112743  Contents
```
この場合、Usersまでは適用されているが、Logsから適用されていないことを示している。