TL;DR
- Laravelの一部コアクラスはsingletonとしてバインドされている
- サービスプロバイダからextendメソッドで拡張することで処理を挟み込める
- stubsのパスを変更することで独自の生成ファイルを定義する
目的
「生成ファイル」とは、 artisan make:migration
コマンドを叩いた際に、
自動で作成されるマイグレーションファイルを指します。
生成された時点で、独自のBlueprintを差し込んでおいたり、
IDをUUID型にしておきたい、などの要望が発生することもあると思います。
本記事では生成ファイルのテンプレート(Stub)を拡張し、
make:migration
を叩くだけで思い通りのマイグレーションファイルを生成する方法を紹介します。
手順
1. サービスプロバイダを生成する
マイグレーションコマンドを拡張するためのサービスプロバイダを作成します。
$ php artisan make:provider MigrationServiceProvider
2. MigrationServiceProviderでコアクラスを拡張
make:migration
は、
Illuminate\Database\Console\Migrations\MigrateMakeCommand
を起点として、
Illuminate\Database\Migrations\MigrationCreator
を呼び出しています。
MigrationCreatorは stubPath
メソッドを持っており、
こちらで Stub のディレクトリパスを返しています。
今回はstubPathメソッドを拡張し、別のディレクトリのStubを読み込ませるよう変更します。
MigrationCreatorはインスタンス化の際、サービスプロバイダに登録済みのSingleton として呼び出されます。
そのため、サービスプロバイダの extend メソッドによって容易に拡張が可能です。
対象となるSingletonは migration.creator です。
extendメソッドの第二引数にあたるクロージャの受け取る引数は、
ひとつ目 ($creator
) が、元のSingletonのインスタンス、
ふたつ目 ($app
) が、コンテナインスタンスとなります。
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と異なる場合は適宜パスを変更してください。
$ cp vendor/laravel/framework/src/Illuminate/Database/Migrations/stubs/ resources/stubs/Migration -R
4. Stubを編集
あとはStubを編集するだけです。
DummyClass、DummyTable は後から適宜置換される部分なので編集しないよう気を付けてください。
下記例では、Schema Facadeを独自拡張に変更し、
IDの初期型をbigIncrementに変更、
論理削除カラムを追加しています。
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を実行
$ php artisan make:migration CreateUser --create users
Created Migration: yyyy_mm_dd_xxxxxxxx_create_user
<?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