Laravel Bleezeでユーザ情報のカラムを追加する時にやること

## 経緯
Laravelでスタジオ予約サイトを作成した時に、アカウント管理でBleezeを導入しました。
その際、デフォルトのユーザ情報（名前、メールアドレス、パスワード）に加えて、生年月日などアカウントに持たせる情報を追加したい時にやることをまとめました。

## 環境
- Laravel 11.23.5(Laravel sailを使用)
- SQlite 3.43.2
- Docker 27.2.0
## アカウント作成機能

### 1. データベースのマイグレーションを更新
#### 一般的な方法
users テーブルに必要な項目（例：grade や birthday）を追加するために、マイグレーションファイルを作成します。
``` bash:bash
sail artisan make:migration add_profile_fields_to_users_table --table=users
```
このマイグレーションファイルに新しいカラムを追加します。
```php:database/migrations/xxxx_xx_xx_xxxxxx_add_profile_fields_to_users_table.php

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

return new class extends Migration {
    public function up(): void {
        Schema::table('users', function (Blueprint $table) {
            $table->integer('grade')->nullable();   // 学年
            $table->date('birthday')->nullable();    // 生年月日
        });
    }

    public function down(): void {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn(['grade', 'birthday']);
        });
    }
};
```
次に、マイグレーションを実行してテーブルを更新します。
```bash:bash
sail artisan migrate
```

他のサイトを見たりAIに質問すると大体この方法が記載されているのですが、
この方法だとマイグレーションファイルが増えて煩雑になると感じたので今回の場合のみ以下の方法を取りました。
#### 今回行った方法

##### 1. 一度データベースのマイグレーションを作成前に戻す（ロールバック）
今回はまだ他のデータベースを作っていなかったので一度全てのマイグレーションをロールバックしました。
```bash:bash
# 一つ前のmigrationを取り消す
sail artisan migrate:rollback
# 全てのマイグレーションを取り消す
sail artisan migrate:reset
```
##### 2 マイグレーションファイルを直接編集する。
次に、該当のマイグレーションファイル（今回はusers）を直接編集します。

```php:database/migrations/0001_01_01_000000_create_users_table.php
<?php

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

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');

            // 追加したカラム
            $table->integer('grade');
            $table->boolean('admin')->default(false);
            $table->datetime('birthday')->nullable(true);

            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

        Schema::create('password_reset_tokens', function (Blueprint $table) {
            $table->string('email')->primary();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });

        Schema::create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->foreignId('user_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->longText('payload');
            $table->integer('last_activity')->index();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
        Schema::dropIfExists('password_reset_tokens');
        Schema::dropIfExists('sessions');
    }
};

```
:::note warn
チーム開発では予期せぬエラーの原因になるので他の人が作成したマイグレーションファイルを無闇に編集するのはやめた方がいいです。
:::

### 2. RegisterController に新しい項目のバリデーションを追加

RegisteredUserController.phpで新しいフィールドをバリデートし、登録時に保存するようにします。
```php:app/Http/Controllers/Auth/RegisteredUserController.php

use App\Models\User;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;

public function store(Request $request)
{
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],

            // 追加した項目
            'birthday'=>['date_format:Y-m-d'],
            "grade"=>['required','integer','min:1','max:4'],

            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
        'grade' => $request->grade,
        'birthday' => $request->birthday,
    ]);

    event(new Registered($user));

    Auth::login($user);

    return redirect(RouteServiceProvider::HOME);
}
```
:::note info
ここに追加するのはアカウント作成フォームで入力を求められる項目のみです。
（今回の場合adminなど）それ以外の項目を入れると正常に動作しないのでご注意ください。
:::
### 3. Bladeファイルに入力項目を追加

 resources/views/auth/register.blade.php に新しい項目の入力欄を追加します。
```php:resources/views/auth/register.blade.php
<!-- 学年 -->
<div class="mt-4">
    <x-input-label for="grade" :value="__('Grade')" />
    <select id="grade" name="grade" class="block mt-1 w-full" required>
        <option value="1">1回生</option>
        <option value="2">2回生</option>
        <option value="3">3回生</option>
        <option value="4">4回生</option>
    </select>
    <x-input-error :messages="$errors->get('grade')" class="mt-2" />
</div>

<!-- 生年月日 -->
<div class="mt-4">
    <x-input-label for="birthday" :value="__('Birthday')" />
    <x-text-input id="birthday" class="block mt-1 w-full" type="date" name="birthday" :value="old('birthday')" required />
    <x-input-error :messages="$errors->get('birthday')" class="mt-2" />
</div>
```
### 4. モデルにフィルアブルプロパティを追加

最後に、Userモデルに追加したカラムを $fillable に登録しておきます。
```php:app/Models/User.php

protected $fillable = [
    'name',
    'email',
    'password',
    'grade',       // 追加
    'birthday',    // 追加
];
```

## アカウント編集機能
### 1. 編集フォームのBladeテンプレートの更新
Breezeのprofile/editビューに新しい入力項目を追加します。次のようにgradeとbirthdayフィールドを追加してください。
```php:resources/views/profile/partials/update-profile-information-form.blade.php

<div>
    <x-input-label for="grade" :value="__('Grade')" />
    <select id="grade" name="grade" class="block mt-1 w-full" required>
        <option value="1" {{ old('grade', $user->grade) == 1 ? 'selected' : '' }}>1回生</option>
        <option value="2" {{ old('grade', $user->grade) == 2 ? 'selected' : '' }}>2回生</option>
        <option value="3" {{ old('grade', $user->grade) == 3 ? 'selected' : '' }}>3回生</option>
        <option value="4" {{ old('grade', $user->grade) == 4 ? 'selected' : '' }}>4回生</option>
    </select>
    <x-input-error :messages="$errors->get('grade')" class="mt-2" />
</div>

<div>
    <x-input-label for="birthday" :value="__('Birthday')" />
    <x-text-input id="birthday" name="birthday" type="date" class="mt-1 block w-full" :value="old('birthday', $user->birthday ? $user->birthday->format('Y-m-d') : '')" required />
    <x-input-error :messages="$errors->get('birthday')" class="mt-2" />
</div>
```

### 2. ProfileUpdateRequestの編集
ProfileUpdateRequestファイルに、gradeやbirthdayに対するバリデーションルールを追加します。

```php:app/Http/Requests/ProfileUpdateRequest.php

public function rules(): array
{
    return [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($this->user()->id)],
        'grade' => ['required', 'integer', 'min:1', 'max:4'],     // 学年のバリデーション
        'birthday' => ['required', 'date'],                        // 生年月日のバリデーション
    ];
}
``` 
これで正常に実装できます。

## 最後に
この手順で私の環境では正常に実装できたと思います。
不正確な情報がございましたらコメントでご教示願いますと幸いです。