Laravel 5.5 make:migration コマンドの生成ファイルを改造する方法

# TL;DR
* Laravelの一部コアクラスはsingletonとしてバインドされている
* サービスプロバイダからextendメソッドで拡張することで処理を挟み込める
* stubsのパスを変更することで独自の生成ファイルを定義する

# 目的
「生成ファイル」とは、 **```artisan make:migration```** コマンドを叩いた際に、
自動で作成されるマイグレーションファイルを指します。

生成された時点で、独自のBlueprintを差し込んでおいたり、
IDをUUID型にしておきたい、などの要望が発生することもあると思います。

本記事では生成ファイルのテンプレート（**Stub**）を拡張し、
```make:migration``` を叩くだけで思い通りのマイグレーションファイルを生成する方法を紹介します。

# 手順

## 1. サービスプロバイダを生成する

マイグレーションコマンドを拡張するためのサービスプロバイダを作成します。

```bash
$ php artisan make:provider MigrationServiceProvider
```

## 2. MigrationServiceProviderでコアクラスを拡張

```make:migration``` は、
```Illuminate\Database\Console\Migrations\MigrateMakeCommand``` を起点として、
```Illuminate\Database\Migrations\MigrationCreator``` を呼び出しています。

_https://github.com/laravel/framework/blob/a5903123344e8d6fc9ebab2447800348003d8536/src/Illuminate/Database/Migrations/MigrationCreator.php#L188-L191_

MigrationCreatorは **```stubPath```** メソッドを持っており、
こちらで **Stub** のディレクトリパスを返しています。
今回はstubPathメソッドを拡張し、別のディレクトリのStubを読み込ませるよう変更します。

MigrationCreatorはインスタンス化の際、**サービスプロバイダに登録済みのSingleton** として呼び出されます。
そのため、サービスプロバイダの **extend** メソッドによって容易に拡張が可能です。
対象となるSingletonは **migration.creator** です。

extendメソッドの第二引数にあたるクロージャの受け取る引数は、
ひとつ目 （```$creator```） が、元のSingletonのインスタンス、
ふたつ目 （```$app```） が、コンテナインスタンスとなります。

```php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Migrations\MigrationCreator;

class MigrateServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->extend('migration.creator', function ($creator, $app) {
            return new class ($app['files']) extends MigrationCreator
            {
                public function stubPath()
                {
                    return resource_path() . '/stubs/Migration';
                }
            };
        });
    }
}
```

今回はアプリケーションの、**```resources/stubs/Migration```** にパスを変更しました。

## 3. Stubをコピー
アプリケーションルートで以下コマンドを実行し、フレームワークコアからStubファイルをコピーします。
**blank.stub / create.stub / update.stub** の3つのファイルが含まれます。
composerパッケージのインストール先がvendorと異なる場合は適宜パスを変更してください。

```bash
$ cp vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs/ resources/stubs/Migration -R
```

## 4. Stubを編集

あとはStubを編集するだけです。
**DummyClass**、**DummyTable** は後から適宜置換される部分なので編集しないよう気を付けてください。

下記例では、Schema Facadeを独自拡張に変更し、
IDの初期型をbigIncrementに変更、
論理削除カラムを追加しています。

```php:create.stub
use App\Facades\CustomSchema as Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class DummyClass extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('DummyTable', function (Blueprint $table) {
            $table->bigIncrement('id');
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('DummyTable');
    }
}
```

## 5. make:migrationを実行

```bash
$ php artisan make:migration CreateUser --create users

Created Migration: yyyy_mm_dd_xxxxxxxx_create_user
```

```php:yyyy_mm_dd_xxxxxxxx_create_user.php
<?php

use App\Facades\CustomSchema as Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUser extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrement('id');
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
```

# 備考
extendを利用すると、フレームワークコアでコンテナに登録されたインスタンスを何でも拡張できます。
今回の例では最初のインスタンスを捨てて拡張クラスを再インスタンス化していますが、
無駄なコストを消費するので、何度も呼び出されるインスタンスに対してこのような拡張の仕方をするのは避けましょう。

# 参考資料
https://stackoverflow.com/questions/34423170/override-singleton-in-laravel-container
https://code.tutsplus.com/tutorials/digging-in-to-laravels-ioc-container--cms-22167