人狼GM支援ツールを作る（Laravel-マイグレーション編）

※この記事は[人狼GM支援ツールを作る（環境構築編）](https://qiita.com/3894eye/items/9e9b84bdfc5449296652)の続きとなります。

# はじめに
前回、Laravel Homesteadを利用してVM上に環境を構築しました。
今回から実装に入るのですが、まずは私が最初につまづいたDBのマイグレーションとLaravelのモデルから整理していきます。

以下、各ソフトウェアのバージョンは

- PHP 7.2.5
- MySQL 5.7.22
- Laravel 5.6

です。



# マイグレーション
今回用意した環境（Laravel Homestead）には、デフォルトでMySQLが用意されています。ユーザ名とパスワードもあらかじめ設定してある状態です。
接続情報はプロジェクト内の`.env`ファイルに書かれています
今回は、あらかじめ用意したテーブル定義に沿ってテーブルを作成していきます。

テーブル定義の例は以下のようになります。

- room表

|カラム       |型   |制約         |
|------------|-----|-------------|
| room_id    | int | Primary Key |
| player_num | int | Not Null    |

- player表

|カラム       |型   |制約         |
|------------|-----|-------------|
| player_id  | int | Primary Key |
| player_name| varchar | Not Null|
| room_id    | int | Foreign Key |



## テーブルを作成する
早速定義通りにテーブルを作成していきます。
以下のコマンドをプロジェクトのルートで実行します。

```bash:command
> php artisan make:migration CreateRoomTable
```
このコマンドで、`database/migrations` に `{timestamp}_create_room_table.php` というファイルが生成されます。

```php:{timestamp}_create_room_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateRoomTable extends Migration
{
    /**
     * マイグレーション実行
     *
     * @return void
     */
    public function up()
    {
        Schema::create('room', function (Blueprint $table) {
            // ここにテーブル定義を記述
        });
    }

    /**
     * マイグレーションを元に戻す
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('room');
    }
}
```

日本語ドキュメントを参考にしつつ、（[Laravel 5.6 データベース：マイグレーション](https://readouble.com/laravel/5.6/ja/migrations.html)）createメソッドのクロージャ内にカラム名と型、制約などをバシバシ記述していきます。

```php:{timestamp}_create_room_table.php（記述後を抜粋）
public function up()
{
    Schema::create('room', function (Blueprint $table) {
        $table->increments('room_id');
        $table->integer('player_num');
    });
}
```

`increments(カラム名)` はMySQLの `AUTO_INCREMENT` に対応しています。
この調子で、Player表の定義も書いていきます

```php:{timestamp}_create_player_table.php（記述後を抜粋）
public function up()
{
    Schema::create('player', function (Blueprint $table) {
        $table->increments('player_id');
        $table->integer('room_id')->unsigned();
        $table->string('player_name', 10);

        $table->foreign('room_id')->references('room_id')->on('room')->onDelete('cascade');
    });
}
```
ここで注意すべき点として、外部キーに指定したカラムの型があります。
`room_id` は符号なしINTなので、外部キーでもそれに合わせてやる必要があります。
加えて、`$table->foreign('room_id')->references('room_id')->on('room')->onDelete('cascade');` で外部キー制約を指定します。

## 初期値を挿入する
一部のテーブルには、あらかじめデータを入れておきたいことがあります。（マスタ表とか）
そこで、テーブルに初期値を挿入するシーダクラスを生成し、テーブル定義同様に書いていきます。

```bash:command
> php artisan make:seeder HogeMstSeeder
```

このコマンドをプロジェクトルートで実行すると、`database/seeds` に `HogeMstSeeder.php`が生成されます。
やはり日本語ドキュメントを参考にしつつ、（[Laravel 5.6 データベース：シーディング](https://readouble.com/laravel/5.6/ja/seeding.html)）初期値を書いておきます。

## マイグレーションを実行する
一通りのテーブル定義と初期値設定を書いたら、マイグレーションを実行してMySQLにデータを流し込みます。

```bash:command
> php artisan migrate --seed
```

SQLのエラーが吐き出されなければ成功です。
これで、PHPのコードを書くだけでテーブル生成と値の挿入ができました。



## エラー吐いたんだけど……？
もしエラーを吐いた場合、原因として以下のものが挙げられます。

- 外部キーの型があってない
- 外部キー制約が追加できない

前者のエラーは前述のとおり、外部キーに指定したカラムにunsigned()をつなげてやれば解決します。

後者のエラーの場合、外部キーで指定したいテーブルがまだ作成されていない可能性があります。
というのも、マイグレーションの実行順は **タイムスタンプ順が古い順** だからです。
手あたり次第にテーブル定義を書き散らすとハマります。（ハマりました）

私はファイル名のタイムスタンプをいい感じに弄って実行順を変えることで解決しました。
※力技です。もっとスマートに解決できると思います。

また、再度マイグレーションを実行する際は `migrate:fresh` か `migrate:refresh` のコマンドを使います。
`migrate:fresh` では今あるテーブルをすべてdropして再度実行し、`migrate:refresh` ではすべての操作をロールバックして再度実行するという違いがあります。



# モデル生成
今までで作成したテーブルに対するモデルクラスを生成します。
以下のコマンドでRoom表に対するモデルが生成されます。

```bash:command
> php artisan make:model Room
```

これで、`app`ディレクトリ下に`Room.php`が生成されます。
Laravelでは、モデルクラスをどこに配置してもよい、というルールになっていますので、各々好きなところに配置してください。

このモデルクラスですが、eloquentというORMになっています。
（eloquentの解説はここでは省略します。余裕があれば後日……）

生成したままの状態では、

- CalemCase単数形のクラス名がsnake_case複数形のテーブル名と紐づく（ex. `RoleStatus`クラス → `role_statuses`表）
- 主キーのカラム名は`id`

だと認識されます。これを実際のテーブル定義に合わせるよう、モデルクラスに追記していきます。

```php:Room.php
class Room extends Model
{
    protected $table = 'room';                              // (1)
    protected $primaryKey = 'room_id';                      // (2)
    protected $fillable = [ /* insertを許可するカラム名 */];  // (3)
    public $timestamps = false;                             // (4)
}
```

(1)ではテーブル名、(2)では主キーの変数を書き換えています。
(3)のように`$fillable`に対して追加すると、挿入を許可するカラムを配列にまとめて指定することで予期せぬ操作を防ぐことができます。
(4)では、`created_at`と`updated_at`という2つのカラムを使わないよう指定しています。今回のようにテーブル定義にこれらのカラムを含めなかった場合は、この指定を忘れるとモデルを操作したときにエラーを吐きます。



# 準備完了！
eloquentの操作やリレーションの定義といった話をかなーり省略しましたが、これでデータベースとその操作の準備は完了です。
SQLを一行たりとも書かずに、データベースにテーブルを作成することができました！


# おまけ
今回はテーブル生成 → モデルクラス生成の順に作業しました。
実はこれを同時に行うコマンドがあります。

```bash:command
> php artisan make:model Room -m
```

このコマンドで、`Room.php`と`{timestamp}_create_rooms_table.php`が同時に生成されます。
※rooms、と複数形になっていることに注目！

テーブル定義の際、主キーのカラム名は`id`に、外部キーのカラム名は`{参照するテーブル名}_id`としておくと便利です。



# 参考にしたサイト

- [Laravel 5.6 データベース：マイグレーション](https://readouble.com/laravel/5.6/ja/migrations.html)
- [Laravel 5.6 データベース：シーディング](https://readouble.com/laravel/5.6/ja/seeding.html)
- [Laravel 5.6 Eloquent：利用の開始](https://readouble.com/laravel/5.6/ja/eloquent.html)