Laravelで使用するコマンドまとめ

# はじめに
速習Laravelを読み、Laravelの勉強をしたので、備忘録としてコマンドをまとめておきます。

# テーブルを追加
テーブルを追加して、テストデータを投入するまでの一連のコマンドをまとめます。

### マイグレーションファイルを作成
`php artisan make:migration create_items_table`

### マイグレーションファイルを編集
`database/migrations`にファイルが作成されるのでファイルを編集する
```php
// 一部抜粋
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('items', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->integer('price');
            $table->timestamps();
        });
    }
```

### マイグレーションを実行
`php artisan migrate`

### モデルを作成
`php artisan make:model Item`

:::note info
モデルは`app/Models`に作成されます。
中身は何も書かれていませんが、EloquentというORMがモデル名からデータベースのテーブルを解決して、DBのテーブルを参照します。(Itemの場合は、itemsというテーブルを参照します）
:::

### テストデータ作成のためのファクトリーを作成
`php artisan make:factory ItemFactory`

### ファクトリーを編集
`database/factories`に作成されるので、データを生成する定義を行います
```php
<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Item>
 */
class ItemFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => $this->faker->word(),
            'price' => $this->faker->numberBetween(1000, 5000),
        ];
    }
}
```

:::note info
ダミーデータを作成するため、fakerを使用しています。
他にも調べてみると、便利そうなものがたくさんありました。
https://www.wantedly.com/companies/logical-studio/post_articles/916638
:::

### モデルを編集
ファクトリーを使用するためモデルを編集します
```diff_php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
+ use Illuminate\Database\Eloquent\Factories\HasFactory;

class Item extends Model
{
    //
+   use HasFactory;
}
```

### テストデータを追加するためのSeederを作成
`php artisan make:seeder ItemssTableSeeder`

### Seederを編集
`database/seeders`に作成されるので編集します。
```php
<?php

namespace Database\Seeders;

use App\Models\Item;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Seeder;

class ItemsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        DB::table('items')->truncate();
        Item::factory()->count(10)->create();
    }
}
```

### テストデータを投入
`php artisan db:seed --class=ItemsTableSeeder`

### 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2475427/2367022d-e234-77ae-d5d1-515332effe1a.png)


# APIを作成

### APIの機能をインストール
`php artisan install:api`

### リクエストを作成
`php artisan make:request CreateItemRequest`

:::note warn
リクエストのクラスは作成しなくても問題ないですが、バリデーションを使用する場合は必要となります。専用のリクエストクラスを作成し、オーバーライドしないと、バリデーションエラー時リダイレクトされるようです。
:::

### リクエストの中身を実装
```php:app/Http/Requsts/CreateItemRequest
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Contracts\Validation\Validator;

class CreateItemRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'name' => 'required|string|max:100',
            'price' => 'required|numeric|min:0',
        ];
    }

    protected function failedValidation(Validator $validator)
    {
        $response['errors']  = $validator->errors()->toArray();

        throw new HttpResponseException(response()->json($response, 422));
    }
}
```

### コントローラー作成
`php artisan make:controller ItemController`

### コントローラの中身を実装
`app/HTTP/Controllers`に作成されます

```php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\CreateItemRequest;
use App\Models\Item;

class ItemController extends Controller
{
    public function create(CreateItemRequest $req)
    {
        $validated = $req->validated();
        $item = new Item();
        $item->name = $validated['name'];
        $item->price = $validated['price'];
        $item->save();

        return response()->json([
            'message' => 'Item created successfully!',
            'data' => $item,
        ], 201);
    }
}
```

### APIのルートを定義
`routes/api.php`に以下を追加します。
```php:routes/api.php
Route::post('/items', '\App\Http\Controllers\ItemController@create');
```

:::note info
api.phpに定義すると、/api/booksのようなパスになる。
また、web.phpに定義した場合、POSTするときにCSRFがないとエラーになるが、api.phpはなくても問題ない。

`php artisan route:list`で定義しているルート一覧を確認できる
:::

### APIを実行
```
 curl -X POST localhost:8000/api/items -H "Content-Type: application/json" -d '{"name": "test", "price": 3000}'
 ```

# ミドルウェアを追加
### ミドルウェアを作成
```
php artisan make:middleware LogMiddleware
```

### ミドルウェア実装
作成すると、`app/Http/Middleware`にファイルが作成されるので、実装します
```php
    public function handle(Request $request, Closure $next): Response
    {
        $method = $request->method();
        $path = $request->path();
        file_put_contents(storage_path('logs/access.log'), date('Y-m-d H:i:s') . " $method $path\n", FILE_APPEND);
        return $next($request);
    }
```

### 登録
ルートに登録します。
```app/routes/api.php
Route::get('/books/{id}', '\App\Http\Controllers\BookController@getBook')->whereNumber('id')->middleware(LogMiddleware::class);
// 複数の場合 
Route::get('/books/{id}', '\App\Http\Controllers\BookController@getBook')->whereNumber('id')->middleware(LogMiddleware::class, AnyMiddleware::class);
```

### アクション後に実行する場合
アクション後に実行する場合は、実装したミドルウェアを下記にようにします。
```php
    public function handle(Request $request, Closure $next): Response
    {
        // $method = $request->method();
        // $path = $request->path();
        // file_put_contents(storage_path('logs/access.log'), date('Y-m-d H:i:s') . " $method $path\n", FILE_APPEND);
        $response = $next($request);

        // レスポンス内容を取得
        $content = $response->getContent();
        file_put_contents(storage_path('logs/access.log'), date('Y-m-d H:i:s') . " $content\n", FILE_APPEND);
        return $response;
    }
```

### 他の登録方法
#### グローバル
`app/bootstrap/app.php`で定義する
```php
<?php

use App\Http\Middleware\LogMiddleware;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__ . '/../routes/web.php',
        api: __DIR__ . '/../routes/api.php',
        commands: __DIR__ . '/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(LogMiddleware::class);
    })
    ->withExceptions(function (Exceptions $exceptions) {
        //
    })->create();
```
#### グループ
ルート定義でgroupを登録できます。
```php:app/routes/api.php
Route::middleware([\App\Http\Middleware\LogMiddleware::class])->group(function () {
    Route::post('/books', '\App\Http\Controllers\BookController@create');
    Route::get('/books/{id}', '\App\Http\Controllers\BookController@getBook')->whereNumber('id')->middleware(LogMiddleware::class);
});
Route::get('/books', '\App\Http\Controllers\BookController@getBooks');
Route::post('/items', '\App\Http\Controllers\ItemController@create');
```