【Laravel Breeze】認証ユーザー情報を取得・表示する方法

# はじめに
こんにちは！Laravelをメインとする自社開発スタートアップ企業で働き始めたレイと申します。

Laravelへの理解を深めるために個人開発を行っているのですが、Breezeを使って認証機能を実装したものの、ユーザー情報を取得・表示する方法で少し詰まってしまいました。

そこで、今回はLaravel Breezeで認証したユーザー情報を取得・表示する方法について、備忘録も兼ねてまとめました。

Laravel Breezeを導入したばかりの初心者の方や、ユーザー情報の取得・表示方法で悩んでいる方の参考になれば幸いです。

# 前提/対象読者/環境
- Laravel 11
- Docker + Nginx リバースプロキシ環境
- Laravel初学者
- BreezeでUserテーブル(1)、記事投稿のためのArticleテーブル(多)を用意

# Breeze
Breezeは、Laravelで用意されている認証に関するライブラリで、簡単に認証機能を導入することができます。

Laravel Breezeの詳しい説明は、公式ドキュメントを参照してください。

https://readouble.com/laravel/11.x/ja/starter-kits.html

## Breezeの導入
Laravel Breezeを導入するには、ターミナルで次のコマンドを実行します。
※開発環境に合わせてください。
```
docker compose exec app bash
composer require laravel/breeze --dev
php artisan breeze:install
```

これだけでbreezeの導入が完了し、ビュー画面でログインや新規登録ページができているのわかり、エディタ上でたくさんのフォルダやファイルができているのがわかります。

## Breezejpの導入

Laravel Breezeはデフォルトで英語表記ですが、「breezejp」を導入することで日本語表記にすることができます。

https://github.com/askdkc/breezejp

こちらも下記コマンドで簡単に導入できて、翻訳もすぐにできます。

```
docker compose exec app composer require askdkc/breezejp --dev
php artisan breezejp
```

さらにお好みで言語切替機能も追加ができて、もし導入するなら下記コマンドを実行します。

```
php artisan breezejp --langswitch
```

# ユーザー情報の取得・表示
さてここからが本題で、ユーザー情報や全体情報を取得する方法についてまとめています。

## 各フォルダ・ファイルのコード
先に用意してあるフォルダ・ファイルをまとめました。これらを編集して実装していきます。ユーザー情報の取得・表示だけ見たいという方はスキップしてください。

### Articleのマイグレーションファイル
```:src/database/migrations/日付_create_articles_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('articles', function (Blueprint $table) {
            $table->id(); // 主キー
            $table->foreignId('user_id')
                ->constrained('users') // usersテーブルのidを参照
                ->cascadeOnDelete();   // ユーザー削除時に関連する記事も削除
            $table->string('title', 100); // 記事のタイトル（最大100文字）
            $table->text('body');         // マークダウン形式の本文（ユーザー入力）
            $table->text('html_content')->nullable(); // HTML変換後の本文（表示用キャッシュ）
            $table->boolean('is_published')->default(false); // 公開状態（false:下書き, true:公開）
            $table->timestamps();         // 作成日時と更新日時（created_at, updated_at）
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('articles');
    }
};
```

### Articleモデル
```:src/app/Models/Article.php
<?php

namespace App\Models;

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

class Article extends Model
{
    use HasFactory;

    // 各列の登録を許可する(ホワイトリスト)
    protected $fillable = ['user_id', 'title', 'body', 'html_content', 'is_published', 'created_at'];
}
```

### Userモデル
```:src/app/Models/User.php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    /** @use HasFactory<\Database\Factories\UserFactory> */
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var list<string>
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var list<string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}
```

### Articleコントローラー
```:src/app/Http/Controllers/ArticleController.php
<?php

namespace App\Http\Controllers;

use App\Models\Article;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;

class ArticleController extends Controller
{
    public function index(): View
    {
        $articles = Article->all();

        return view('articles.index', [
            'articles' => $articles
        ]);
    }

    public function create()
    {
        return view('articles.create');
    }

    public function store(Request $request): RedirectResponse
    {
        $article = new Article();
        $article->title = $request->title;
        $article->body = $request->body;
        $article->is_published = $request->is_published;
        $article->save();

        return redirect('/article');
    }
}
```

### Articleのindexビューファイル
```:src/resources/views/articles/index.blade.php
<div class="w-full md:p-6 p-4">
    <h2 class="text-4xl font-bold text-center">記事一覧</h2><!-- ページタイトル -->
    <div class="grid grid-flow-col grid-row2 gap-6 mt-6">
        <div class="row-span-2 row-end-3 border border-primary text-center rounded-lg p-6">
            <h3 class="text-2xl font-bold">ユーザー名：{{ $user->name }}</h3><!-- 作成者 -->
            <p>hogehogehogehogehogehogehogehogehoge<br>hogehogehogehogehoge</p>
        </div>
        <div class="row-start-1 row-end-4 flex flex-col flex-grow gap-6">
            @foreach ($articles as $article)
                <div class="p-6 justify-center rounded-lg border border-primary">
                    <h3 class="text-2xl font-bold">{{ $article->title }}</h3><!-- 記事タイトル -->
                    <p class="p-3 text-main">作成者：{{ $article->user->name }}</p><!-- 作成者 -->
                    <p class="p-3 text-[#00BFFF]">{{ $article->is_published }}</p><!-- 下書き・公開 -->
                    <p class="p-3 text-main">{{ $article->created_at }}</p><!-- 作成日 -->
                </div>
            @endforeach
        </div>
    </div>
</div>
```

### ルーティング
```:src/routes/web.php
<?php

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

Route::get('/', function () {
    return view('top');
})->name('top');

Route::get('/dashboard', function () {
    return view('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');
    Route::resource('/article', ArticleController::class);
});

require __DIR__.'/auth.php';
```

## 認証しているユーザー情報のみを取得する
流れとして、モデルへのアソシエーションを定義とコントローラーにてORMで定義し、ビューファイルで取り出します。

### 1.テーブル設計とモデル定義
今回は、UserテーブルとArticleテーブルの2つのテーブルを使用します。

- Userテーブル: ユーザー情報を保存
- Articleテーブル: 記事情報を保存

UserテーブルとArticleテーブルは、1対多の関係にあります。つまり、1人のユーザーが複数の記事を作成することができます。

この関係をLaravelのEloquent ORMで表現するために、モデルにアソシエーションを定義します。

#### Userモデル
articleに対して**hasMany**の記述をし、関数名を複数形にします。

```:src/app/Models/User.php
<?php

// 省略

class User extends Authenticatable
{
    // 長いので省略

    // 追加
    public function articles() {
        return $this->hasMany(Article::class);
    }
}
```

#### Articleモデル
userに対して**belongsTo**の記述をします。

```:src/app/Models/Article.php
<?php

// 省略

class Article extends Model
{
    // 省略

    // 追加
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

### 2.コントローラー設定

#### indexメソッド
indexメソッドでは、認証済みユーザーの情報を取得し、そのユーザーが作成した記事一覧を取得します。Authファザードのuserメソッドを使用します。

```:src/app/Http/Controllers/ArticleController.php
use App\Models\Article;
use Illuminate\Support\Facades\Auth;

public function index(): View
{
    $user = Auth::user(); // 認証済みユーザー情報取得
    $articles = $user->articles; // 認証済みユーザーの記事取得

    return view('articles.index', [
        'articles' => $articles,
        'user' => $user
    ]);
}
```

#### storeメソッド
storeメソッドでは、フォームから送信された記事情報をデータベースに保存します。

```:src/app/Http/Controllers/ArticleController.php
public function store(Request $request): RedirectResponse
{
    $article = new Article();
    $article->user_id = Auth::user()->id; // 外部キー指定
    $article->title = $request->title;
    $article->body = $request->body;
    $article->is_published = $request->is_published;
    $article->save();

    return redirect('/article');
}
```

#### コントローラー全体
最終的なArticleコントローラーです。
```:src/app/Http/Controllers/ArticleController.php
<?php

namespace App\Http\Controllers;

use App\Models\Article;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;

class ArticleController extends Controller
{
    public function index(): View
    {
        $user = Auth::user(); // 認証済みユーザー情報取得
        $articles = $user->articles; // 認証済みユーザーの記事取得

        return view('articles.index', [
            'articles' => $articles,
            'user' => $user
        ]);
    }

    public function create()
    {
        return view('articles.create');
    }

    public function store(Request $request): RedirectResponse
    {
        // リクエストから情報取得、保存
        $article = new Article();
        $article->user_id = Auth::user()->id;
        $article->title = $request->title;
        $article->body = $request->body;
        $article->is_published = $request->is_published;
        $article->save();

        // 処理後リダイレクト
        return redirect('/article');
    }
}

```
### 3.ビューファイル

#### indexファイル
index.blade.phpでは、コントローラーから渡されたユーザー情報と記事一覧を表示します。

```:src/resources/views/articles/index.blade.php
<div class="w-full md:p-6 p-4">
    <h2 class="text-4xl font-bold text-center">記事一覧</h2>
    <div class="grid grid-flow-col grid-row2 gap-6 mt-6">
        <div class="row-span-2 row-end-3 border border-primary text-center rounded-lg p-6">
            // userのname取り出し
            <h3 class="text-2xl font-bold">ユーザー名：{{ $user->name }}</h3>
        </div>
        <div class="row-start-1 row-end-4 flex flex-col flex-grow gap-6">
            // 連想配列に格納して各情報を取り出す
            @foreach ($articles as $article)
                <div class="p-6 justify-center rounded-lg border border-primary">
                    // articleのtitle取り出し
                    <h3 class="text-2xl font-bold">{{ $article->title }}</h3>
                    // articleのis_published取り出し
                    <p class="p-3 text-[#00BFFF]">{{ $article->is_published }}</p>
                    // articleのcreate_at取り出し
                    <p class="p-3 text-main">{{ $article->created_at }}</p>
                </div>
            @endforeach
        </div>
    </div>
</div>
```

### 表示の確認
ブラウザで記事一覧ページにアクセスし、認証済みユーザーの情報と記事一覧が表示されることを確認します。

![スクリーンショット 2025-02-16 7.54.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3772961/c0a8b2b4-8144-47c7-b2d4-3922498ade1e.png)

## 全ての記事情報を取得・表示する場合
全ての記事情報を取得・表示するには、コントローラーで認証済みユーザーの情報を取得する処理を削除し、`Article`モデルから全ての記事を取得するように変更します。

### Articleコントローラー

```:src/app/Http/Controllers/ArticleController.php
public function index(): View
{
    $articles = Article::all();

    return view('articles.index', [
        'articles' => $articles,
        'user' => $user // この変数はビューで使われているため、削除しない
    ]);
}
```

### 表示の確認
ブラウザで記事一覧ページにアクセスし、全てのユーザーの記事情報が一覧表示されることを確認します。

![スクリーンショット 2025-02-16 7.50.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3772961/71c4c365-f6de-404e-82bd-0a61014b7bcc.png)


# おわりに
今回は、Laravel Breezeで認証したユーザー情報を取得・表示する方法について解説しました。

頭を悩ませて向き合ったことで、やっと認証から一覧と作成機能ができたので、引き続き開発してアウトプットして試行錯誤してパワーアップしていきます。

この記事が、Laravel Breezeを導入したばかりの初心者の方や、ユーザー情報の取得・表示方法で悩んでいる方の参考になれば幸いです。

もし、この記事の内容に不備や改善点などがありましたら、ご指摘いただけると嬉しいです。

# 参考
Laravelドキュメント「認証」

https://laravel.com/docs/11.x/authentication

Readoubleマニュアル「認証」

https://readouble.com/laravel/11.x/ja/authentication.html

https://qiita.com/ucan-lab/items/a7441bff64ff1f173c10