Laravelアプリに後から認証機能を入れる

## 謝辞
この記事は、以下のページを元にToDoアプリを作成し、後から認証機能を入れた過程を記したものです。

https://zenn.dev/eguchi244_dev/books/laravel-tutorial-books

まずはじめに、著者のeguchi244様にお礼を申し上げます。
自分のような初学者にとって、大いに勉強になりました。

## ToDoアプリの画面
まず初めに、上記のページを読みながら作れるToDoアプリは次のような画面になっています。
![スクリーンショット 2024-11-17 10.32.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821993/cfbae4f7-beb4-635e-2719-a950ced26ddf.png)

ToDoの単位を`タスク`として扱い、複数のタスクを入れておける`フォルダ`があります。
トップページからは以下が行えます。

- フォルダの追加、編集、削除
- フォルダ内のタスクの追加、編集、削除
- タスクには状態（未着手／着手中／完了）と期限を設定できる

## 後からユーザ認証機能を入れる
ユーザごとにToDoを管理したい場合は、ユーザ認証機能を実装する必要があります。
参考とさせていただいたページでも、後から`Laravel/ui`を使ってユーザ認証機能を入れています。
本記事では`Breeze`を入れてみたので、その過程を書いていこうと思います。

:::note info
使用しているバージョン
Laravel Framework 11.21.0
PHP 8.3.12 (cli) (built: Oct  7 2024 10:53:51) (NTS)
:::

### Breezeのインストール
上記のToDoアプリを作成した際に、Breezeパッケージはインストールされていたようなので、`php artisan breeze:install`コマンドを実行します。
すると、ユーザ認証に必要なコントローラなどが新規作成されます。

:::note warn
ルーティングを上書きしてしまうので、手動でマージが必要です
:::

### ルーティング

```php:routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\TaskController;
use App\Http\Controllers\FolderController;
use App\Http\Controllers\DivideController;

Route::get('/', function () {
    return view('welcome');
});

// ログイン後ユーザのフォルダ有無を判定して遷移先を決定する
Route::controller(DivideController::class)->group(function () {
    Route::get('/divide', 'index')->name('divide');
})->middleware(['auth', 'verified']);

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');

    // フォルダ一覧、作成、編集、削除
    Route::get('/folders/{id}/tasks', [TaskController::class, 'index'])->name('tasks.index');
    Route::get('/folders/create', [FolderController::class, 'create'])->name('folder.create');
    Route::post('/folders', [FolderController::class, 'store']);
    Route::get('/folders/{id}/edit', [FolderController::class, 'edit'])->name('folder.edit');
    Route::post('/folders/{id}/update', [FolderController::class, 'update'])->name('folder.update');
    Route::delete('/folders/{id}', [FolderController::class, 'destroy'])->name('folder.delete');

    // タスク作成、編集、削除
    Route::get('/folders/{id}/tasks/create', [TaskController::class, 'create'])->name('tasks.create');
    Route::post('/folders/{id}/tasks/', [TaskController::class, 'store'])->name('tasks.store');
    Route::get('/folders/{folder_id}/tasks/{task_id}/edit', [TaskController::class, 'edit'])->name('tasks.edit');
    Route::post('/folders/{folder_id}/tasks/{task_id}/update', [TaskController::class, 'update'])->name('tasks.update');
    Route::delete('/folders/{folder_id}/tasks/{task_id}', [TaskController::class, 'destroy'])->name('tasks.delete');
});

require __DIR__.'/auth.php';

```
ルートページにはログイン画面へのリンクがあり、それ以外のページは認可されていないとアクセスできないようになっています。
もし、ログイン前の状態でアクセスした場合には、ログイン画面へリダイレクトされます。

そして、ログイン後には`divide`ルートにリダイレクトします。

```php:app/Http/Controllers/DivideController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use App\Models\User;


class DivideController extends Controller
{
    /**
     * ログイン後判定
     * ユーザに紐づくフォルダの存在有無でリダイレクト先を振り分け
     */
    public function index(): RedirectResponse
    {
        $user = Auth::user();
        $first_folder = $user->folders->first();

        if (!is_null($first_folder)) {
            // フォルダ一覧画面
            return redirect()->route('tasks.index', [$first_folder]);
        } else {
            // フォルダ作成画面
            return redirect()->route('folder.create');
        }
    }

}
```

`DivideController`では、ログイン認証されたユーザのフォルダ有無で判定を行い、フォルダが無ければフォルダ作成ページへ、有ればフォルダ一覧ページにリダイレクトするようにしました。

## 所感
`Breeze`はとても簡単に認証機能を入れることができました。
基本的に`php artisan breeze:install`コマンドを打って、作成されたルーティングに現行のものをマージしただけです。

また、Laravelによって新規作成されるソースを読むことができるのも、魅力の一つだと思いました。