【Laravel】migrationファイル名から日付を取り除く

##はじめに
今回は、表題の方法を解説していきます。

__「2019_07_05_052836_create_users_table.php」が「\_\_\_\_create_users_table.php」のようにスマートになり、マイグレーションの順序も簡単に変更できるようになります。__

お急ぎの方は、[手順](#手順)からどうぞ。
また、完成物は[GitHub](https://github.com/yuuumbk/laravel-original-artisan-command)にあります。

##環境
Laravel Framework 8.64.0

##動機
なぜ、こんなことをしたいと思ったのか？理由は主に二つあります。
###その1
Laravelのマイグレーションは、__ファイルの作成日時の順__に実行されます。そのため、外部キー制約などリレーションの関係があるテーブルを複数扱う際に、正しい順序で実行できずにエラーとなってしまうことが多々あるのです。

###その2
Laravelのデフォルトのファイル名形式は「yyyy_mm_dd_xxxxxx_*.php」となっています。　例えば、「2019_07_05_052836_create_users_table.php」のようなものです。

この形式のどこが問題かというと、エディタなどで長いファイル名が省略された際に、どのテーブルについてのファイルかが一眼で判別できないという点です。

##手順
###雛形の作成
はじめに、以下のコマンドを実行し、雛形を作成してください。（今回は`migrate:fresh`と`migrate:refresh`コマンドについてのみ解説します。他のコマンドに関してもベースは同じです。）

```bash
php artisan make:command ArtisanMigrateFreshCommand
php artisan make:command ArtisanMigrateRefreshCommand
```

`/app/Console/Commands`の中に`ArtisanMigrateFreshCommand.php`と`ArtisanMigrateRefreshCommand.php`があれば成功です。なかった場合は、以下のコードを元に作成してください。

<details><summary>__ArtisanMigrateFreshCommand.php__</summary><div>

```php:ArtisanMigrateFreshCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ArtisanMigrateFreshCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return Command::SUCCESS;
    }
}

```

</div></details>

<details><summary>__ArtisanMigrateRefreshCommand.php__</summary><div>

```php:ArtisanMigrateRefreshCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ArtisanMigrateRefreshCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        return Command::SUCCESS;
    }
}

```

</div></details>

###雛形をコーディング
次に作成されたそれぞれのファイルについてコードを記述していきます。

まず、コマンド名を決定します。`php artisan`の後にくる識別名です。こちらは任意のもので結構なのですが、`migrate:fresh`や`migrate:refresh`としてしまうと、エラーとなってしまいます。すでに用意されているものは使用しないようにしましょう。

```php:ArtisanMigrateFreshCommand.php
  protected $signature = 'migrate:f {--seed}';
```
```php:ArtisanMigrateRefreshCommand.php
  protected $signature = 'migrate:rf {--seed}';
```

`{--seed}`は、マイグレーションと同時にシーディングを行うかのオプションです。


次に、コマンドの説明を設定します。こちらはスルーしても問題ありません。
コマンドで`php artisan list`と打ち、`migrate:fresh`と`migrate:refresh`のdescriptionを確認します。

```:抜粋
php artisan list

.
.
.
migrate:fresh        Drop all tables and re-run all migrations
migrate:refresh      Reset and re-run all migrations
```
これらをそのまま流用します。

```php:ArtisanMigrateFreshCommand.php
  protected $description = 'Drop all tables and re-run all migrations';
```
```php:ArtisanMigrateRefreshCommand.php
  protected $description = 'Reset and re-run all migrations';
```

次に、本命の作業です。実行する処理を記述していきます。

`handle()`にてそれぞれ以下のようにコーディングしてください。（本来であれば共通する処理はまとめるべきなのですが、今回は省略しています）

```php:ArtisanMigrateFreshCommand.php
// 先頭に記述
use Illuminate\Support\Facades\Config;

public function handle() {

    $config = Config::get('migration');
    $migrations = $config['files'];
    $base_path = $config['base_path'];
    $prefix = $config['prefix'];
    $suffix = $config['suffix'];

    $is_first = true;

    foreach ($migrations as $migration) {
    $path = $base_path . $prefix . $migration . $suffix;

    if ($is_first) {
        $cmd = 'migrate:fresh';
        $is_first = false;
    } else {
        $cmd = 'migrate';
    }

    $this->call($cmd, [
        '--path' => $path,
    ]);
    }

    if ($this->option('seed')) {
    $this->call('db:seed');
    }

    return Command::SUCCESS;
}
```
```php:ArtisanMigrateRefreshCommand.php
// 先頭に記述
use Illuminate\Support\Facades\Config;

public function handle() {
    $config = Config::get('migration');
    $migrations = $config['files'];
    $base_path = $config['base_path'];
    $prefix = $config['prefix'];
    $suffix = $config['suffix'];

    $is_first = true;

    foreach ($migrations as $migration) {
    $path = $base_path . $prefix . $migration . $suffix;

    if ($is_first) {
        $cmd = 'migrate:refresh';
        $is_first = false;
    } else {
        $cmd = 'migrate';
    }

    $this->call($cmd, [
        '--path' => $path,
    ]);
    }

    if ($this->option('seed')) {
    $this->call('db:seed');
    }

    return Command::SUCCESS;
}
```

###カーネルさんに登録する
`/app/Console/Kernel.php`に先ほど作成した二つのコマンドを登録します。

```php:Kernel.php
protected $commands = [
    Commands\ArtisanMigrateFreshCommand::class,
    Commands\ArtisanMigrateRefreshCommand::class,
];
```

###設定ファイルの作成
マイグレーション対象となるファイルを一元管理するファイルです。`/database/seeders/DatabaseSeeder.php`と同様の役割を担っている捉えておいてください。

`/config/`に`migration.php`を作成し、以下の雛形をコピペしてください。

```php:migration.php
<?php

return [

    'files' => [
        
    ],

    'base_path' => 'database/migrations/',

    'prefix' => '____',

    'suffix' => '.php',

];

```

`files`配列内にファイル名を実行したい順に列挙していきます。例えば、

```php:migration.php
'files' => [
    'create_users_table',
    'create_plans_table',

    'create_admins_table',
]
```

のような形です。

この形を取ることで、__順序や実行の可否の変更は容易に変更できる__ようになります。

###ファイル名を変更する
最後にファイル名を変更しましょう。

日付部分（yyyy_dd_mm_xxxxxx）を取り除き、先頭にアンダースコア（\_）を**4つ**並べた``____**.php``という形式にします。例えば、``____create_users_table.php``のような形です。

なお、このアンダースコア4つというのは、``/vendor``内の次のコードによるものです。

```php:Migrator.php
protected function getMigrationClass(string $migrationName): string
{
    return Str::studly(implode('_', array_slice(explode('_', $migrationName), 4)));
}

```

##おわりに
以下のコマンドをそれぞれ実行してみましょう！

```
php artisan migrate:fresh
php artisan migrate:fresh --seed
php artisan migrate:refresh
php artisan migrate:refresh --seed
```

エラーが出なければ、無事成功です。

エラーが出てしまった場合は、
「`Kernel.php`にコマンドが登録されているか」
「`config/migration.php`にファイルが設定されているか」
「ファイル名が適切に変更されているか」
「その他手順通りに進められているか」
「マイグレーションファイル自体にエラーの原因がないか」
などを確認してみてください。

##GitHub
今回作成したファイルをまとめました。参考にどうぞ。

https://github.com/yuuumbk/laravel-original-artisan-command