Laravelでクラスのひな形を作成する方法の決定版

## はじめに
この記事はLaravelを使ったクラスのテンプレートファイルの作成についての記事です。
この記事を読み終えた頃には誰でも自由なテンプレートを作れるようになります。

## 対象
- PHP 8.0
- Laravel 8.0

※下位環境でも動作する場合がございます

## 初級編

```
php artisan make:cast クラス名         // 新しいEloquentキャストクラスを作成します
php artisan make:channel クラス名      // 新しいブロードキャストクラスを作成します           
php artisan make:command クラス名      // 新しいコマンドクラスを作成します
php artisan make:component クラス名    // 新しいコンポートネントクラスを作成します
php artisan make:controller クラス名   // 新しいコントローラクラスを作成します
php artisan make:event クラス名        // 新しいイベントクラスを作成します
php artisan make:exception クラス名    // 新しいエクセプションクラスを作成します
php artisan make:factory クラス名      // 新しいファクトリクラスを作成します
php artisan make:job クラス名          // 新しいジョブクラスを作成します
php artisan make:listener クラス名     // 新しいリスナークラスを作成します
php artisan make:mail クラス名         // 新しいメールクラスを作成します
php artisan make:middleware クラス名   // 新しいミドルウェアクラスを作成します
php artisan make:migration クラス名    // 新しいマイグレーションクラスを作成します
php artisan make:model クラス名        // 新しいモデルクラスを作成します
php artisan make:notification クラス名 // 新しいノーティフィケーションクラスを作成します
php artisan make:observer クラス名     // 新しいオブザーバクラスを作成します
php artisan make:policy クラス名       // 新しいポリシークラスを作成します
php artisan make:provider クラス名     // 新しいサービスプロバイダクラスを作成します
php artisan make:request クラス名      // 新しいリクエストクラスを作成します
php artisan make:resource クラス名     // 新しいリソースクラスを作成します
php artisan make:rule クラス名         // 新しいルールクラスを作成します
php artisan make:seeder クラス名       // 新しいシーダークラスを作成します
php artisan make:test クラス名         // 新しいテストクラスを作成します
```
Laravelには各種テンプレートをもとにクラスのひな形を生成する上記のようなコマンドが多数用意されています。
マニュアルや解説本などでもよく紹介されているので使っている人も多いかと思います。

しかし、これらはLaravelを使っている全ユーザー共通のひな形であるため、開発プロジェクトルールに沿っていないというデメリットがあり、慣れてくるとコマンド打つよりも既存のクラスをコピペした方が早いということになります。

## 中級編
初級編であったデメリットの解消法として、Laravel7でLaravelのテンプレートを自由にカスタマイズできる機能が追加されました。

```
php artisan stub:publish
```

このコマンドを実行すると、stubsフォルダの中にテンプレートのもととなっているstubファイルが自動で生成されます。
このstubファイルを改変することで、開発プロジェクトに沿ったひな形に改変（コメントを日本語にする、メソッドを追加する等）することができます。

しかし、stubファイルは単なるテキストファイルであるため、カスタマイズ性が決して高いとはいえません。
またテンプレート内の一部を変数化しておいて外から注入し、出力するクラスごとに内容を少し変えるということもできません。

## 上級編
前置きが長くなりましたが、本記事の本題はここからです。
プロジェクト特有のクラスのひな形を作成するためのコマンドを作成するための方法を紹介します。

##### 特徴
- その1
    - テンプレート内でPHP関数やblade関数を使うことができます。より高度なカスタマイズが可能です
- その2
    - テンプレート内に変数を埋め込んでおき、外からテンプレートファイルへ値を渡すことができます
- その3
    - 出力先も自由にカスタマイズすることができます

##### 手法
Laravelのbladeテンプレートを使ってこれらを実現します。

```php
use File;

// テンプレートの内容が返ってくるので変数に保存（テンプレートへ値を渡すことも可能）
$view = view('template.sample', [
    'className' => 'hogehoge',
])->render();

// 毎回上書きする必要がない場合は、deleteは不要
File::delete(app_path('hogehoge.yml'));

// ファイルを書き出す
if (!File::isFile(app_path('hogehoge.yml'))) {
    File::put(app_path('hogehoge.yml'), $view);
}
```

### サンプル
以下は、テーブル定義のYamlファイルを生成するコマンドです。
応用次第で、どんなひな形も自由に作ることができます。

```app/Console/Commands/Sample.php
<?php

namespace App\Console\Commands;

use DB;
use File;
use Illuminate\Console\Command;

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

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

    /**
     * Execute the console command.
     */
    public function handle(): void
    {
        $schema = DB::connection()->getDoctrineSchemaManager();
        $tableNames = $schema->listTableNames();

        foreach ($tableNames as $tableName) {
            // テーブル情報を取得
            $schema = DB::connection()->getDoctrineSchemaManager();
            $table = $schema->listTableDetails($tableName);
            $columns = $table->getColumns();

            // テンプレートの内容が返ってくるので変数に保存（テンプレートへ値を渡すことも可能）
            $view = view('template.sample', [
                'tableName' => $tableName,
                'columns'   => $columns,
            ])->render();

            // 毎回上書きする必要がない場合は、deleteは不要
            File::delete(app_path($tableName.'.yml'));

            // ファイルを書き出す
            if (!File::isFile(app_path($tableName.'.yml'))) {
                File::put(app_path($tableName.'.yml'), $view);
            }
        }
    }
}
```


```resources/views/template/sample.blade.php
- controller_name: '{{ $tableName }}'
  columns:
@foreach($columns as $column)
    - name: '{{ $column->getName() }}'
      description: '{{ $column->getComment() }}'
      data_type: '{{ $column->getType()->getName() }}'
@endforeach
```

## 補足
https://qiita.com/nagareboshi/items/314a7db7748521d8432f

https://readouble.com/laravel/8.x/ja/blade.html