【Laravel10】breezeでメール認証をテキスト認証に変更する

# はじめに
　Laravel10では`breeze`をインストールすることでログイン認証機能が一瞬で実装できる。すごい時代だ。

```:breezeのインストール
php artisan breeze:install
```

　breezeはメールアドレスとパスワードで認証する仕組みになっているが、ネットに公開しないような、たとえば社内インフラとしてのアプリケーションを構築する場合はメールアドレスではなく社員IDなどテキストデータとパスワードでログインさせたい。仕様を変更したときの備忘録。


# userテーブルに社員IDのカラムを設定する

```
vi database/migrations/2014_10_12_000000_create_users_table.php
```

　以下のようにした。ここでは社員IDを格納するカラムを`num`としている。'user_id'とか'employee_id'とかにしとけば良かったなぁ。

```:database/migrations/2014_10_12_000000_create_users_table.php
    public function up(): void
    {
        if (!Schema::hasTable('users')) {
            Schema::create('users', function (Blueprint $table) {
                $table->id();
                $table->string('num')->unique();
                $table->string('name');
                $table->string('email');
                $table->string('password');
                $table->rememberToken();
                $table->timestamps();
            });
        }
    }
```


# Viewのbladeを編集する
　Laravel10のViewは`Laravel bladeコンポーネント`で記述されており、`Tailwind CSS`が使われているらしい。既存の項目を編集すると見た目や挙動が多少変わるので、それぞれの`input`タグの`class`に"h-10"を追記している。

　スタイルを編集しているので、反映しないなぁってときは編集後に以下のコマンドを。

```
npm run build
```


## 登録画面の編集

```
vi resources/views/auth/register.blade.php
```

　英語で表示される部分をついでに日本語にしています。

```:resources/views/auth/register.blade.php
<x-guest-layout>
    <form method="POST" action="{{ route('register') }}">
        @csrf

        <!-- Name -->
        <div>
            <x-input-label for="name" :value="__('名前')" />
            <x-text-input id="name" class="block mt-1 w-full h-10" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
            <x-input-error :messages="$errors->get('name')" class="mt-2" />
        </div>

        <!-- Employee Number ここを追記しています -->
        <div class="mt-4">
            <x-input-label for="num" :value="__('社員ID')" /> 
            <x-text-input id="num" class="block mt-1 w-full h-10" type="text" name="num" :value="old('num')" />
            <x-input-error :messages="$errors->get('num')" class="mt-2" />
        </div>


        <!-- Email Address -->
        <div class="mt-4">
            <x-input-label for="email" :value="__('Email')" />
            <x-text-input id="email" class="block mt-1 w-full h-10" type="email" name="email" :value="old('email')" required autocomplete="username" />
            <x-input-error :messages="$errors->get('email')" class="mt-2" />
        </div>

        <!-- Password -->
        <div class="mt-4">
            <x-input-label for="password" :value="__('パスワード')" />

            <x-text-input id="password" class="block mt-1 w-full h-10"
                            type="password"
                            name="password"
                            required autocomplete="new-password" />

            <x-input-error :messages="$errors->get('password')" class="mt-2" />
        </div>

～ 以下略 ～
```


## ログイン画面の編集

```
vi resources/views/auth/login.blade.php
```

　Email文字列を`POST`している部分を`num`に変更する。

```:resources/views/auth/login.blade.php
<x-guest-layout>
    <!-- Session Status -->
    <x-auth-session-status class="mb-4" :status="session('status')" />

    <form method="POST" action="{{ route('login') }}">
        @csrf

        <!-- Email Address -->
        <div>
            <x-input-label for="num" :value="__('社員ID')" />
            <x-text-input id="num" class="block mt-1 w-full h-10" type="text" name="num" :value="old('num')" required autofocus autocomplete="username" />
            <x-input-error :messages="$errors->get('num')" class="mt-2" />
        </div>

        <!-- Password -->
        <div class="mt-4">
            <x-input-label for="password" :value="__('パスワード')" />

            <x-text-input id="password" class="block mt-1 w-full h-10"
                            type="password"
                            name="password"
                            required autocomplete="current-password" />

            <x-input-error :messages="$errors->get('password')" class="mt-2" />
        </div>

～ 以下略 ～
```


# リクエスト処理の編集
　認証のリクエスト処理で合っていたら、間違っていたら、の処理は以下のファイルが担当している。

```
vi app/Http/Requests/Auth/LoginRequest.php
```

　`'email'`と記述されている部分をすべて`'num'`に変更する。

```
    public function rules(): array
    {
        return [
            'num' => ['required', 'string'],
            'password' => ['required', 'string'],
        ];
    }


    public function authenticate(): void
    {
        $this->ensureIsNotRateLimited();

        if (! Auth::attempt($this->only('num', 'password'), $this->boolean('remember'))) {
            RateLimiter::hit($this->throttleKey());

            throw ValidationException::withMessages([
                'num' => trans('auth.failed'),
            ]);
        }

        RateLimiter::clear($this->throttleKey());
    }


    public function ensureIsNotRateLimited(): void
    {
        if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
            return;
        }

        event(new Lockout($this));

        $seconds = RateLimiter::availableIn($this->throttleKey());

        throw ValidationException::withMessages([
            'num' => trans('auth.throttle', [
                'seconds' => $seconds,
                'minutes' => ceil($seconds / 60),
            ]),
        ]);
    }


    public function throttleKey(): string
    {
        return Str::transliterate(Str::lower($this->input('num')).'|'.$this->ip());
    }
```

　次の部分を修正する事で、認証失敗時の返答を日本語化できる。

 ```
 - 'num' => trans('auth.failed'),
 
 + 'num' => 'IDまたはパスワードが違います',
 ```

 　本当は言語設定ファイルを修正すれば良いらしいが、どこにあるんだ？？？
  

# Controllerの編集

　Controllerの一覧を見たとき、あまりにもメールに関するものが多くてダメかと思ったが1つ編集すれば大丈夫だった。

```:ls app/Http/Controllers/Auth実行
AuthenticatedSessionController.php  EmailVerificationNotificationController.php  NewPasswordController.php  PasswordResetLinkController.php  VerifyEmailController.php
ConfirmablePasswordController.php   EmailVerificationPromptController.php        PasswordController.php     RegisteredUserController.php
```

　`RegisteredUserController.php`を編集する。

```
vi app/Http/Controllers/Auth/RegisteredUserController.php
```

　`'num'`の設定を追記する。`'unique:'.User::class`が記述されていると重複があった場合にエラーとなる。`max:`の値は長すぎてもアレなので適当に少なくしている。

```:app/Http/Controllers/Auth/RegisteredUserController.php
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'name' => ['required', 'string', 'max:96'],
            'num' => ['required',  'string', 'max:6', 'unique:'.User::class],
            'email' => ['required', 'string', 'email', 'max:96'],
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

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


# 参考

https://qiita.com/haessal/items/54ca3ea37c8df9918780