Laravel の make:model をいい感じに使いたい

こんにちはみなさん

Laravelは割と自由に書かせてくれるので、今作っている分を実装するには楽なんですが、全体として実装が微妙にブレることがあるので、後々変な不整合が出そうで怖くなります。
CakePHPはそのあたりをルールによって縛っていて、よりコード量が少なくなるほど彼らの提供する正しい実装になるようになっています。
まあ、何が言いたいかって言うと、自分の実装をあまり信用していないので、ある程度自動生成してもらえれば、やりやすいんじゃないかなぁって思った次第。

そんなわけで、Laravelで大事なコマンドである `make:model` に注目していきます。

# 三行で

- Laravel artisan コマンドの make:model でモデルを自動生成
- make:model のオプションで必要なファイルのガワを作れる
- モデルの名前空間を変えたい場合はコマンド自作して上書き

# make:model の基本

`make:model` は`Eloquent`モデルの自動生成コマンドです。

```
php artisan make:model Post
```
これを実行すると、app直下に以下のファイルが生成されます。

```php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    //
}
```
app直下に入れたくない場合は名前空間含めて名前を付ける必要があります。

```
php artisan make:model Entities/Post
```
これで、app/Entities配下にPost.phpが生成されます。名前空間も合わせて作られています。

# make:model のオプションで付随したファイルを自動生成

モデルというのは、プロダクトの中心にあるものですので、こいつをもとにしていろんな処理を作っていくことになると思っています。モデルを作ったら、そいつを取得するためのコントローラやデータベースの作成、テスト用のファクトリーとかを作っていくことになるわけですが、そういうのはモデルと一緒に作ってしまえばいいのではと考えるわけです。

どんなオプションがあるかは `-h` オプションで見れます。

```
php artisan make:model -h
Description:
  Create a new Eloquent model class

Usage:
  make:model [options] [--] <name>

Arguments:
  name                  The name of the class

Options:
  -a, --all             Generate a migration, factory, and resource controller for the model
  -c, --controller      Create a new controller for the model
  -f, --factory         Create a new factory for the model
      --force           Create the class even if the model already exists
  -m, --migration       Create a new migration file for the model
  -p, --pivot           Indicates if the generated model should be a custom intermediate table model
  -r, --resource        Indicates if the generated controller should be a resource controller
  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
      --env[=ENV]       The environment the command should run under
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```
まあ、all使えばいいのでは、ってことです。

## -m: マイグレーションを一緒に作る

EloquentモデルはORMなので、たいていなにかのテーブルとひも付きます。また、Eloquentモデルは、テーブル名の指定がない場合は自身の名前の複数形のテーブルを参照するのですが、そのあたりを考えたくない場合は、その規約にあった名前のテーブルのマイグレーションを予め作っておくのが吉です。

というわけで、マイグレーションを一緒に作ります。

```
# php artisan make:model Post -m
Model created successfully.
Created Migration: 2020_01_19_073425_create_posts_table
```
すると、以下のようなファイルができています。

```databases/migrations/2020_01_19_073425_create_posts_table.php
<?php

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

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

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}
```
本当はDjangoみたいにモデルの方でデータ型を指定したいところですが。。。こっちのほうがいい部分もあるので、一概にどっちがいいとは言えませんな。

## -f: ファクトリーを一緒に作る
モデルを作ったら、そいつのファクトリーを一緒に作っておくのがマナーってやつですよね(????)。
それならいっそ、モデルと一緒に作っちゃえばいいでしょう。

```
# php artisan make:model Post -f
Model created successfully.
Factory created successfully.
```
これで次のファイルができています。

```paratest/database/factories/PostFactory.php
<?php

/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Post;
use Faker\Generator as Faker;

$factory->define(Post::class, function (Faker $faker) {
    return [
        //
    ];
});
```
まあ、ただの空箱ですが。

## -c: コントローラを一緒に作る
Webアプリは、極論、Webのリソースに対する操作になりますので、中心となるモデルをどんなふうにいじるかを決定するコントローラを一緒に作る場合があります。
それなら、やっぱり一緒に作っちゃうのが良いのではってことになります。

```
# php artisan make:model Post -c
Model created successfully.
Controller created successfully.
```

これででいるわけですが。。。

```paratest/app/Http/Controllers/PostController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    //
}
```
もう少し、こう、scaffolding じみた感じに色々作ってくれていいのだが。。。

## -r: リソースコントローラも一緒に作る
`-c`オプションだと、ただの空箱コントローラが作られましたが、`-r`オプションを付ければリソースコントローラを作ることができます。

```
fully.
root@334281a14de2:/var/www# php artisan make:model Post -r
Model created successfully.
Controller created successfully.
```
コンソールの結果は何も変わりませんが、出力結果が変わります。

```paratest/app/Http/Controllers/PostController.php
<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function show(Post $post)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function edit(Post $post)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Post $post)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Post  $post
     * @return \Illuminate\Http\Response
     */
    public function destroy(Post $post)
    {
        //
    }
}
```
ここまで作ってくれるのなら、デフォルトのコードも作ってくれればいいのに。。。
もちろん、これを作っただけでは動かないので、あとで`router`に登録するのを忘れないようにしましょう。
`Route::resource('post', 'PostController');`で行けるはずです。

## -a: -m, -f, -r 全部入り
ここまでやってきたやつを全部入れるのなら、`-a`オプションが便利です。

```
# php artisan make:model Post -a
Model created successfully.
Factory created successfully.
Created Migration: 2020_01_19_081214_create_posts_table
Controller created successfully.
```
モデル名さえ決めていれば、残りのファイルは一括自動生成！
これが楽だと思います。

もちろん、プロダクトが成長していき、単純な構造ではすまなくなってしまえば、このようにコントローラまで一緒に全部作るというのは厳しいこともあるかもですが、仮説検証のようなとりあえず試してみたいというときであればこれで良いのではと思います。

# 初期の名前空間を変える

`make:model`はモデル名を指定すると、そのままapp直下にクラスファイルが生成されるわけですが、これがLaravelの教えだとしても、個人的にはモデルにそれなりの名前空間をつけておきたいところ。
例えば、モデルは必ず`App\Entities`に入れるというのであれば、毎回モデルを作るごとに`Entities\ModelName`って入力するのは面倒です。

なので、こんなコードを`app/Console/Commands`配下に入れます。

```MyMakeModel.php
<?php

namespace App\Console\Commands;

use Illuminate\Foundation\Console\ModelMakeCommand;

class MyMakeModel extends ModelMakeCommand
{
    protected function getDefaultNamespace($rootNamespace)
    {
        return $rootNamespace.'\Entities';
    }
}

```
あとは、普通に`php artisan make:model ModelName`というコマンドを叩くだけで、先に設定した名前空間付きのモデルが作られます。
なお、`-r`や`-f`などで、リソースコントローラやファクトリを一緒に作った場合は、モデルの名前空間もちゃんと踏襲してくれていますので、おすすめです。

# まとめ
というわけで、Laravelにおけるモデルの自動生成のちょっとした使い方を見てみました。自分でやってて、割と発見があったかなぁという印象です。
機械的に作れるところはとっとと自動生成しちゃって、肝心のモデルの設計を頑張ろうってところでしょうか。

今回はこんなところです。