僕なりの Breeze 備忘録

# Breeze



## インストール

Laravelアプリケーションができた上でインストールする

```bash
$ sail composer require laravel/breeze --dev
```

Breezeの必要なファイル達をアプリケーション内のそれぞれの適切な場所に公開する

```bash
$ sail php artisan breeze:install
> ミドルウェアを選定できる
```

Breezeで使うUserテーブルをDBにマイグレートする

```bash
$ sail php artisan migrate
```

BreezeやLaravelが依存しているJavaScriptのライブラリやパッケージをインストール

```bash
$ sail npm install
```

デフォルトのスタイリングやJavaScriptの動作をプロジェクトに反映

```bash
$ sail npm run dev
```



## 画面の日本語化

`/resources/views/auth/`以下のviewファイル内に存在する `{{ __('XXXXX')}}`部分に日本語が入ってくるように対応する

### ロケールの変更

```php
# /config/app.php

...省略...
'locale' => 'ja', // ここを変更
...省略...
```

### `/lang/ja.json`

上記ディレクトリとファイルを作成し、以下を追記

```json
// /lang/ja.json

{
    "Log in": "ログイン",
    "Password": "パスワード",
    "Email": "メールアドレス",
    "Forgot your password?": "パスワードをお忘れですか？",
    "Remember me": "パスワードを保存する",
    "Name": "名前",
    "Confirm Password":"パスワードの確認",
    "Already registered?":"登録済ですか？",
    "Register":"登録"
}
```



### ライブラリの利用

> https://github.com/askdkc/breezejp

以下のコマンドを叩く

```bash
$ composer require askdkc/breezejp --dev
$ php artisan breezejp
```

timezoneやlocaleが変わりつつ、langディレクトリが作成され、その中に`ja.json`等のファイルが作成される





## ユーザ名でログインできるように

### 認証バリデーションを変更

`rules()`メソッド内を変更した上で、同ファイルの他の`email`と記述がある部分を`name`に変更

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

class LoginRequest extends FormRequest
{
	...省略...
  
    public function rules(): array
    {
        return [
            'name' => ['required', 'string'], // ここを変更
            'password' => ['required', 'string'],
        ];
    }

  ...省略...
}
```

### Viewの変更

`email`と記載のある部分を`name`に変更

日本語化してあれば、`:value="__('Name')"`で、してなければ `value="お名前"`

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

	...省略...
    
        <!-- Email Address -->
        <div> // ここ以下変更
            <x-input-label for="name" :value="__('Name')" /> 
            <x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="username" />
            <x-input-error :messages="$errors->get('name')" class="mt-2" />
        </div>
              
  ...省略...
```





## 電話番号も登録する

### Viewの対応

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

				...省略...
        <!-- Phone -->
        <div class="mt-4">
            <x-input-label for="phone" :value="__('Phone')" />

            <x-text-input id="phone" class="block mt-1 w-full"
                            type="text"
                            name="phone"
                            :value="old('phone')"
                            required
                            autocomplete="username" />

            <x-input-error :messages="$errors->get('phone')" class="mt-2" />
        </div>
        ...省略...
```

日本語化対応する

```json
// /lang/ja.json

{
	...省略...
  
    "Phone": "電話番号", // ここを追加
  
	...省略...
}
```

### マイグレーションの対応

`users`テーブルに電話番号フィールドを追加する新しいマイグレーションを作成する

```bash
$ php artisan make:migration add_phone_field_to_users_table
```

```php
# /database/migrations/YYYY_mm_dd_HHiiss_add_phone_field_to_users_table.php

return new class extends Migration
{
  
		...省略...
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('phone')->nullable();
        });
    }
		...省略...
      
};
```

```bash
$ php artisan migrate
```

### コントローラーの対応

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

class RegisteredUserController extends Controller
{
  	...省略...
    public function store(Request $request): RedirectResponse
    {
        $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class],
            'phone' => ['required', 'string', 'max:255'], // ここを追加
            'password' => ['required', 'confirmed', Rules\Password::defaults()],
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'phone' => $request->phone,  // ここを追加
            'password' => Hash::make($request->password),
        ]);

        event(new Registered($user));

        Auth::login($user);

        return redirect(RouteServiceProvider::HOME);
    }
}

```

### モデルの対応

```php
# /app/Models/User.php

class User extends Authenticatable
{
		...省略...
    protected $fillable = [
        'name',
        'email',
        'phone', // ここを追加
        'password',
    ];
		...省略...
}
```





## メール認証を有効にする

### MustVerifyEmailインターフェースを実装

```php
# /app/Models/User.php

use Illuminate\Contracts\Auth\MustVerifyEmail; // ここを追加

class User extends Authenticatable implements MustVerifyEmail // ここを変更
{
	...省略...
}

```

### routeの対応

認証されていないユーザーへのアクセスを制限するミドルウェアを追加する

authミドルウェアはゲストからのアクセスを防ぐ、verifiedミドルウェアはメールを認証したかどうかチェックする

```php
# /app/Models/User.php

Route::get('/only-verified', function () {
   return view('only-verified');
})->middleware(['auth', 'verified']); // ここを追加
```

### Mailpitで確認

http://localhost:8025/ にアクセスすることで確認できる



## マルチ認証（Multi Authentication）

>https://reffect.co.jp/laravel/breeze_multi_auth/#laravel-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90

adminsとmembersのようにひとつのアプリケーションで２つの別のシステムを扱う時や機能を提供する時に、それぞれのシステムにログインできる人＝その機能を使える人を制御したい時に実装する。

### モデル作成（＝DBテーブルの作成）

マイグレーションファイルを作成しながらモデルを作成する

```bash
$ php artisan make:model Admin -m
```

Userモデルの内容をAdminモデルにコピペ（クラス名はAdminに変更）

User用のマイグレーションファイルの内容をAdmin用のマイグレーションファイルにコピペ

DBテーブルの作成

```bash
$ php artisan migrate
```

### ガード（Guard）の設定

Laravelにアクセスするリクエストに対してガードする（守る）役目を持っている。

どのロールにおいての認証であるかを表現するのに使う（Laravelではguardとして表現するのが自然）

`/config/auth.php`に追記する

### URLでどのロールかを判別する

`/member`は会員向けの機能、`/admin`は管理者向けの機能を提供するとして、`/app/detect_role.php`というヘルパーを作成する

```php
# /app/detect_role.php

<?php

use Illuminate\Support\Str;

if (!function_exists('detect_role')) {

    function detect_role(): ?string
    {
        $current_uri = request()->path();
        $roles = ['member', 'admin'];

        foreach ($roles as $role) {
            if (Str::startsWith($current_uri, $role)) {
                return $role;
            }
        }

        return null;
    }
}

```

### 登録したヘルパー関数をどこでも使えるようにする

```json
// /composer.json

{
    ... 省略 ...
    "autoload": {
        "files": [
            "app/detect_role.php"
        ],
	... 省略 ...
    },
    ... 省略 ...
}
```

### routesの設定

`/routes/member.php`と`/routes/admin.php`を作成する

`/routes/web.php`の認証関連の記述箇所を移行しながら、PrefixとMiddlewareを設定しながらグループ化する

```php
# /routes/member.php

<?php

use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

Route::prefix('member')->name('member.')->group(function () {
    Route::middleware(['auth:member'])->group(function () {

        Route::get('/dashboard', function () {
            return view('Member/dashboard');
        })->middleware(['auth', 'verified'])
            ->name('dashboard');

        Route::middleware('auth')->group(function () {
            Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
            Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
            Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
        });
    });

    require __DIR__ . '/auth.php';
});

```

### Controllerの設定

**共通利用箇所を親クラスで用意する。**

それぞれのAuth以下のコントローラーはこのクラスを継承する。

```php
# /app/Http/Controllers/Auth/AuthController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Support\Str;

class AuthController extends Controller
{
    private ?string $role;
    private string $package;

    public function __construct()
    {
        $this->role = detect_role();

        if (empty($this->role)) {
            $this->package = '';
        } else {
            $camel = Str::camel($this->role);
            $camel[0] = strtoupper($camel[0]);
            $this->package = $camel;
        }
    }
}
```



**全ロールでAuth以下のコントローラーを利用できるように以下5か所を修正する。**

HOMEの設定

```php
# /app/Providers/RouteServiceProvider.php

class RouteServiceProvider extends ServiceProvider
{
    ... 省略 ...
    private const HOME = '/dashboard';

    public static function home() { // ここを追加
        $role = detect_role();
        return empty($role) ? self::HOME : '/' . $role . self::HOME;
    }
    ... 省略 ...
}

# /app/Http/Controllers/Auth/AuthenticatedSessionController.php

    public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

        $request->session()->regenerate();

        return redirect()->intended(RouteServiceProvider::home()); // ここを変更
    }
```

ルーティングの対応

```php
# /app/Http/Controllers/Auth/NewPasswordController

    public function store(Request $request): RedirectResponse
    {
        ... 省略 ...
        if ($status == Password::PASSWORD_RESET) {
            return redirect()->route($this->route('login'))->with('status', __($status)); // ここを変更
        }
        ... 省略 ...
    }

# /app/Http/Controllers/Auth/AuthController.php

<?php
... 省略 ...
class AuthController extends Controller
{
    ... 省略 ...
    public function route($route): string // ここを追加
    {
        if (empty($this->role)) {
            return $route;
        }
        return $this->role . '.' . $route;
    }
    ... 省略 ...
}
```

レンダリングするリソース名の対応




# Laravel Breeze認証 でログイン後に遷移するページをカスタマイズする

```php
public const HOME = '/home';
```