【Laravel】初めての認証（Laravel8でJetstreamを使う）

- Laravel8でのJetstreamを使った、ユーザー管理機能の学習の記録。「 **Jetstream 導入 と ユーザー登録/更新/ログイン機能** 」についての備忘録です。
- 参考：[Laravel8](https://laravel.com/docs/8.x/frontend#introduction) 、 [Jetstream](https://jetstream.laravel.com/1.x/introduction.html)

# 環境
- OS ： macOS Caralina 10.15.7
- PHP ： 7.4.11
- laravel ： 8.11.2
- Composer ： 1.10.6

# 今回の学習内容

|No.|概要|
|:--:|:--|
|1.|**[Jetstream の導入](#1-jetstreamの導入)**<br>・ 導入のコマンド<br>・ 日本語化する<br>・ 動作確認で、デフォルト機能を確認（ 新規登録/ログイン（email+PW認証）、ユーザー情報更新）|
|2.|**[作成されたファイルの把握](#2-作成されたファイルたちの一部)**<br>※ 大量にファイルが作成されるので、理解が追いついてない。。|
|3.| **[例）基本の実装いろいろ](#3-例基本の実装いろいろ)**<br>・ ログイン / アカウント登録後のリダイレクト先 の変更<br>・ usersテーブルへのカラム追加<br>・ user - 他モデル との紐付け<br>・ 所望のページにアクセス制限をつける<br>・ Jetstream ロゴの変更<br>・ アイコン写真の登録|

# Auth とは？
- 認証（Ahthentication）や認可（Ahthorization）の意味。
- Laravelでは、デフォルトで認証機能があり、コマンドで作れる。

# Jetstream とは？
- Laravel8の認証ライブラリ。
  - Laravel〜5：make:auth、Laravel6〜7：laravel/uiに分離。Laravel8では、全部入り（Jetstream）になったんだとか。。
- Jetstream がやってくれることは、 ログイン、新規登録、メール検証、２段階認証、セッション管理、APIサポート(Laravel Sanctum)、チーム管理など。めっちゃやってくれる。。



# 1. Jetstreamの導入
## 【 導入のコマンド 】

```php:Jetstreamの導入まで
// ① Jetstream のインストール
% composer require laravel/jetstream      // composerを使ってインストール
% php -d memory_limit=-1 /usr/local/bin/composer require laravel/jetstream     // メモリ制限エラーが出たら、メモリを上げてインストール
% laravel new project-name --jet          // アプリの雛形作成のタイミングで、Jetstream をインストールも可能（Livewire/Inertiaやチーム機能を使うか?聞かれる）
// インストールが完了したら、artisan コマンドでjetstreamが使えるようになる

// ② セットアップ ： JSのパッケージをインストール（ livewire と inertia を選択できる）
% php artisan jetstream:install livewire
% php artisan jetstream:install livewire --teams  // チーム機能を使う場合
% php artisan jetstream:install inertia          // vueやreactなどを使う場合は、コッチらしい。。

// ③ マイグレーション実行
% php artisan migrate            // usersテーブル、password_resetsテーブル作成のマイグレーションファイルが、自動生成されてる

// ④ JSとCSSをビルド
% npm install && npm run dev    // npmコマンドが無いエラーが出たら、Nodeのインストールが必要（https://nodejs.org/ja/）
  // 若しくは、別々にNode.js と NPM をインストール
  % npm install
  % npm run dev
// public/css/app.css、 public/js/app.js2ファイルが作成される
```
- ここで、ブラウザで動作確認すると、エラー発生。'Laravel\Sanctum\HasApiTokens' が見つからんらしい。。

> `Trait 'Laravel\Sanctum\HasApiTokens' not found`

→ laravel/sanctum をインストールしてみる。

```php:laravel/sanctumのインストール
// ⑤ laravel/sanctumのインストール
% composer require laravel/sanctum
% php -d memory_limit=-1 /usr/local/bin/composer require laravel/sanctum   // メモリ制限エラーが出たら、メモリを上げてインストール
```
- デフォルトでは、welcomeページに、ログイン・新規登録ボタンが設置してくれてる。が、今回は、既存アプリに導入したので、ルートページは、welcomeページではない。
  - welcomeページに設置された、「 ログイン・新規登録ボタン 」 部分をコピペさせてもらう。

```php:welcomeページに設置された、ログイン・新規登録ボタン部分（welcome.blade.php）
@if (Route::has('login'))
  <div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
    @auth    // ログイン中の場合
      <a href="{{ url('/dashboard') }}" class="text-sm text-gray-700 underline">ダッシュボード</a>
    @else    // 未ログインの場合
      <a href="{{ route('login') }}" class="text-sm text-gray-700 underline">ログイン</a>
      @if (Route::has('register'))
        <a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">アカウント作成</a>
      @endif
    @endif
  </div>
@endif
```
- `＠auth...＠endif`構文（※ Laravel7までは、＠auth...＠endauth）。




## 【 日本語化する 】
**フィールドの日本語化**

1. バリデーションなどの翻訳データが公開されてるので、[GitHubのLaravel-Lang/lang](https://github.com/Laravel-Lang/lang/) から、zipファイルをダウンロード。
1. ダウンロードしたフォルダの中から、日本語化ファイルを移動↓  
<img src="https://gyazo.com/6316e8ada2b87bc0b20e590b555ab4d0/raw" width=40%>
  - コマンドで実行する場合は、↓

```php:日本語化設定（resources/lang/jaの生成）
php -r "copy('https://readouble.com/laravel/6.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');"
php -f install-ja-lang.php
php -r "unlink('install-ja-lang.php');"
// resources/lang/ja に、ファイルが生成される（ auth.php、 pagination.php、 passwords.php、 validation.php ）
```
※ ただし、微妙に英語が残ってるので、気付いた箇所は、resources/lang/js.json に、力技で日本語化。

## 【 動作確認 】
- この段階で、新規登録とログイン機能が動く状態になってる。
- ルートページに、ログイン・新規登録ボタンが出現。  
<img src="https://gyazo.com/1737b1bae7b729e03e9da937a60f5eeb/raw" width =40%>

|新規登録ボタン → 新規登録ページ|ログインボタン → ログインページ|
|:--:|:--:|
|localhost:8000/register|localhost:8000/login|
|<img src="https://gyazo.com/feeaed754113f099674ce884584b8426/raw">|<img src="https://gyazo.com/26021598d0291fbfd71dd674d074fefc/raw">|
|↓ Already registered? をクリック<br>（localhost:8000/login）|↓ Forgot your password? をクリック<br>（localhost:8000/forgot-password）|
|ログインページへ遷移。<br><img src="https://gyazo.com/26021598d0291fbfd71dd674d074fefc/raw">|PWリセットリンクをメールで送信してくれる。<br><img src="https://gyazo.com/1cc1287225efa953f59d7dc27c673639/raw">|

- name、email、PWは必須。
  - name、emailは、Max255文字まで。
  - emailは、文字列長（@を含む）と、一意性。
  - PWは、8文字 / 空 / PW（確認用）と不一致 で、バリデーション。
  - バリデーションの条件は、app\Actions\Fortify\CrateNewUser.php、PasswordValidationRules.php に書かれてる。
- ログイン時に、入力を何度も間違うと、一定時間ログインできなくなる。
- アカウント作成クリックで、DBにデータが保存できてる。
- ログイン / アカウント作成 をクリック → dashboardページに遷移する。

|dashboardページ|ログイン状態 → ルートページ|
|:--:|:--:|
|localhost:8000/dashboard|localhost:8000/|
|<img src="https://gyazo.com/45f332f33daf19985fd881e2285c2437/raw">|<img src="https://gyazo.com/c60d1f47d07b09a0f73c4ac7092299b8/raw"><br>ログイン時のボタン表示|

- dashboardページには、Profile・ログアウトボタンが設置されてる。

|Profileボタン → ユーザー情報更新ページ|ログアウトボタン|
|:--:|:--:|
|localhost:8000/user/profile|localhost:8000|
|<img src="https://gyazo.com/1d449c097360b125058e6c0d3ae94297/raw"><br>ログイン中の端末もわかる。|<img src="https://gyazo.com/1737b1bae7b729e03e9da937a60f5eeb/raw"><br>ルートページへ遷移。<br>（未ログイン時のボタン表示）|

- ユーザー情報（name、email、photo）やPWの更新に関するバリデーション条件は、app\Actions\Fortify\UpdateUserProfileInfomation.php、UpdateUserPassword.php（※ PasswordValidationRules.phpを呼び出してる） に書かれてる。





# 2. 作成されたファイルたちの一部
#### ○ ルーティング
- 認証用のルートが追加される。
- authミドルウェアによって、未ログイン時は、ログインページにリダイレクトされる仕様。

```php:追加される認証用のルート
// web.php
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard', function () {
    return view('dashboard');
})->name('dashboard');   // dashboard というルート名が付けられてる

// api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
```

#### ○ コントローラー （ app\Http\Controllers）
- Jetstream では、不要なので作成されない。必要に応じて、自分で作成する。

```php:例）app/Http/Controllers/Auth/LoginController.phpの作成の場合
% php artisan make:controller Auth\\LoginController
```

#### ○ ビュー （ resources\views ）
- Jetstreamが作成した、コンポーネントファイルは、隠しファイルになってる。 ↓のコマンドで表示でき、編集可能になる。

```php:隠しファイルの表示コマンド
% php artisan vendor:publish --tag=jetstream-views
// resources/views/vendor/jetstream ディレクトリが出現する
```

|ファイル名|用途|
|:--|:--|
|auth\forgot-password.blade.php|PWリセットリンクの送信先入力フォーム|
|auth\login.blade.php | ログインページ<br>layouts\guest.blade.phpを読み込んでる。|
|auth\register.blade.php | 新規登録ページ<br>layouts\guest.blade.phpを読み込んでる。|
|auth\reset-password.blade.php | PWリセット|
|auth\two-factor-challenge.blade.php|ログインページ（２段階認証）|
|auth\verify-email.blade.php | 本人確認ページ（2段階認証）|
|layouts\app.blade.php|ログイン時のレイアウト<br>（ナビゲージョンバー、ヘッダーを呼び出してる）|
|layouts\guest.blade.php|未ログイン時のレイアウト（ログイン・新規登録ページ）|
|profile\●●.blade.php|プロフィール編集ページに使ってる部分テンプレート|
|vendor\jetstream\components\●●.blade.php（隠しディレクトリ）|コンポーネントファイル（各種部分テンプレート）|
|welcome.blade.php|welcome画面に認証用ボタンが設置される。<br>vendor\jetstream\components\welcome.blade.phpからコンポーネントを読み込んでる。|
|dashboard.blade.php|ダッシュボードページ<br>layouts\app.blade.php、welcome.blade.phpを読み込んでる。|
|navigation-dropdown.blade.php|ナビゲージョンバー|

- ＠componentで呼び出していたコンポーネントを`<x-xxxx />`の形式で呼び出す仕組み。Livewireでは、全力でコンポーネントを使い倒してる。。
  - `x-`タグは、Bladeコンポーネント。`x-jet-`タグは、Jetstream内で定義されてるコンポーネント。
  - 例えば、`<x-app-layout>`は、app/View/Components/AppLayout.php を読み込んでる。
このファイルでは、resources/views/layouts/app.blade.php のビューを呼び出してるカタチになってる。
  - 例えば、`<x-jet-welcome />`は、Jetstreamの隠しファイル内のresources/views/vender/jetstream/components/welcome.blade.php を読み込んでる。

#### ○ モデル （ app\Models\User.php ）

```php:作成されたUserモデルの中身
// 値を代入するフィールドの設定
protected $fillable = ['name', 'email', 'password',];
// データを取得しないフィールドの指定
protected $hidden = ['password', 'remember_token', 'two_factor_recovery_codes', 'two_factor_secret',];
// キャストが必要なフィールドの指定
protected $casts = ['email_verified_at'=>'datetime',];
// モデルの配列に追加
protected $appends = ['profile_photo_url',];
```

#### ○ 認証機能の設定 （ config\auth.php ）

```php:認証設定ファイル（config/auth.php）の中身
return [
  'defaults' => [                 // デフォルトの認証「ガード」と、PWリセットオプション
    'guard' => 'web',
    'passwords' => 'users',
  ],
  'guards' => [
    'web' => [
      'driver' => 'session',
      'provider' => 'users',
    ],
    'api' => [
      'driver' => 'token',
      'provider' => 'users',
      'hash' => false,           // APIトークンを平文ではなく SHA-256 にする場合は true
    ],
  ],
  'providers' => [
    'users' => [
      'driver' => 'eloquent',              // データへのアクセスで使用するドライバー
      'model' => App\Models\User::class,   // 指定したドライバーが eloquent の場合、使うモデル
    ],
  ],
  'passwords' => [
    'users' => [
      'provider' => 'users',              // 使うproviderの設定
      'table' => 'password_resets',       // PWリセットトークンの格納テーブル
      'expire' => 60,                     // 有効時間
      'throttle' => 60,                   // 要求制限、スロットル時間（秒）
    ],
  ],
  'password_timeout' => 10800,           // PW再確認までの時間
];
```

#### ○ プロバイダー （ app\Providers ）
- Jetstream を導入すると、認証に関するFortify、Jetstream プロバイダーが追加される。

#### ○ テーブル
- users テーブル ： ユーザ管理用

|カラム名|カラム型|用途|
|:--|:--|:--|
|name|string|ユーザー名|
|email|string（一意性）|メールアドレス|
|email_verified_at|timestamp|メールによる本人確認日時|
|password|string|パスワード|
|two_factor_secret|text（null可）|２段階認証|
|two_factor_recovory_codes|text（null可）|リカバリーコード|
|remember_token|remember_token|クッキー情報|
|current_team_id|foreignId（null可）|チーム機能で使用|
|profile_photo_path|text（null可）|プロフィールのアイコン写真のパス|

- password_resets テーブル ： PWリセット管理用

|カラム名|カラム型|用途|
|:--|:--|:--|
|email|string（インデックス）|メールアドレス|
|token|string|PWリセットトークン|

- sessions テーブル ： セッション管理用

|カラム名|カラム型|用途|
|:--|:--|:--|
|id|string（一意性）|ユーザー識別用のラベルみたいなヤツ|
|user_id|foreignId（一意性、インデックス）|ユーザーid（外部キー）|
|ip_address|string（null可）|アクセスしたIPアドレス|
|user_agent|text（null可）|アクセスしたブラウザ情報|
|payload|text|Sessionに保存する値（base64でエンコードされてる）|
|last_activity|integer（インデックス）|最後のアクセス日時（unixタイムスタンプ）|

# 3. 例）基本の実装いろいろ
## 【 ログイン / アカウント登録後のリダイレクト先 の変更 】
- デフォルトだと、ログインやアカウント登録 → localhost:8000/dashboard にリダイレクトされる。
- 認証ユーザーリダイレクトの設定ファイル（app\Http\Middleware\RedirectIfAuthenticated.php） → 定義元（app\Providers\RouteServiceProvider.php）を呼び出してるカタチ。

```php:app\Http\Middleware\RedirectIfAuthenticated.php
public function handle(Request $request, Closure $next, ...$guards) {
  $guards = empty($guards) ? [null] : $guards;
  foreach ($guards as $guard) {
    if (Auth::guard($guard)->check()) {
      return redirect(RouteServiceProvider::HOME);   // RouteServiceProvider を呼び出してる
    }
  }
  return $next($request);
}
```
なので、定義元の方を変更してみる。

```php:ログインやアカウント登録後のリダイレクト先の変更（app\Providers\RouteServiceProvider.php）
public const HOME = '/home';        // Jetstream導入前
public const HOME = '/dashboard';   // Jetstream導入後
  ↓ // rootページに変更
public const HOME = '/';
```

## 【 usersテーブルへのカラム追加 】
- 登録項目を追加してみる。

|新規登録ページ|プロフィール編集ページ|
|:--:|:--:|
|localhost:8000/register|localhost:8000/user/profile|
|<img src="https://gyazo.com/3861c8d87bbcac31e0faca95a2944d7f/raw" width=70%>|<img src="https://gyazo.com/720163b9b0ef4b3be2b1c213448dabcb/raw">|


- まずは、通常通り、マイグレーションファイルを作成し、usersテーブルにカラムを追加。

```php:usersテーブルにカラム追加
% php artisan make:migration add_column_to_users_table --table=users

// マイグレーションファイルに、追加カラムを記述
public function up() {
  Schema::table('users', function (Blueprint $table) {
    $table->date('birth')->nullable();
  });
}
public function down() {
  Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('birth');
  });
}

% php artisan migrate      // マイグレーション実行
```
次に、新規登録時に、データを保存できるよう、修正。

```php:新規登録のための修正
// Userモデル
protected $fillable = [
     :
  'birth',
];

// app\Actions\Fortify\CreateNewUser.php
Validator::make($input, [
      : 
  'birth' => ['nullable', 'date', 'before:yesterday'],   // null or 昨日以前 でない場合は、バリデーション
])->validate();
return User::create([
      :
  'birth' => $input['birth'],
]);

// ビューに入力フォームを設置（resources\views\auth\register.blade.php）
<div class="mt-4">
  <x-jet-label for="birth" value="{{ __('生年月日（※ 任意）') }}" />
  <x-jet-input id="birth" class="block mt-1 w-full" type="date" name="birth" required />
</div>
```

- 同様に、プロフィール編集ページにも反映。

```php:プロフィール編集ページの修正
// app\Actions\Fortify\UpdateUserProfileInformation.php
Validator::make($input, [
         :
  'birth' => ['required', 'date', 'before:yesterday'],    // 削除メソッド未実装なので、とりあえず、空にできないよーにしとく。。 
])->validateWithBag('updateProfileInformation');

if ($input['email'] !== $user->email &&
         :
} else {
  $user->forceFill([
         :
    'birth' => $input['birth'],
  ])->save();
}

// ビュー（resources\views\profile\update-profile-information-form.blade.php）
<div class="col-span-6 sm:col-span-4">
  <x-jet-label for="birth" value="{{ __('生年月日') }}" />
  <x-jet-input id="birth" type="date" class="mt-1 block w-full" wire:model.defer="state.birth" />
  <x-jet-input-error for="birth" class="mt-2" />
</div>
```




## 【 モデルの紐付け 】
- 例えば、記事投稿にユーザーを紐付ける場合。
  - 紐付けたいテーブルに、user_idカラムを作成し、モデルにアソシエーションを追記。

```php:Userモデルとのアソシエーション
public function user() {
  return $this->belongsTo('App\Models\User');
}
```
```php:コントローラーで取得して、ビューで表示してみる
// モデルを紐づけたので、コントローラーでは、Article::all()で、ユーザー情報も取得できてる
public function index() {
  $articles = Article::all();
  return view('index', ['articles' => $articles] );
}

// ビューで呼び出す
@foreach ($articles as $article)
  <p>{{ $article -> user -> name }}</p>    // 紐づくユーザー名 を表示
@endforeach
```




## 【 アクセス制限 】
- 例えば、投稿・編集・削除機能は、ログイン時のみに制限したい。

```php:コントローラー
class ArticleController extends Controller {
  // 未ログイン時は、index、showのみ許可する（それ以外では、未ログインなら、loginページへリダイレクト）
  public function __construct() {      //  __construct クラスを追加
    $this->middleware('auth')->except(['index', 'show']);
  }

  // 投稿機能
  public function store(Request $request){
    $article = new Article();
    $user = \Auth::user();
    $article -> content = request('content');
    $article -> user_id = $user->id;
    $article -> save();
    return redirect() -> route('article.detail', ['article' => $article->id] );
  }

  // 投稿者のみ、詳細表示ページに、編集・削除ボタンを表示させたいので、ビューでの条件分岐のために、変数で定義
  public function show($id){
    $article = Article::find($id);
    $user = \Auth::user();
    if ($user) {
      $login_user_id = $user->id;
    } else {
      $login_user_id = "";
    }
    return view('show', ['article' => $article, 'login_user_id' => $login_user_id] );
  } 
}
```
```php:ビュー
// ログイン時のみ、登録ボタンを表示する
@auth
  <div>
    <a href='{{ route("article.new") }}'>登録ボタン</a>
  </div>
@endauth

// 投稿者のみ、編集ボタン・削除ボタンを表示する
@auth
  @if ($article -> user_id == $login_user_id)   // 現在のユーザーid　= 投稿者id の場合
    <a href = '{{ route("article.edit", ["article" => $article->id]) }}', class='btn'>編集ボタン</a>
    {{ Form::open(['method' => 'delete', 'route' => ['article.destroy', $article->id] ]) }}
      {{ Form::submit('削除ボタン', ['class' => 'btn']) }}
    {{ Form::close() }}
  @endif
@endauth
```
- 動作確認用に、usersテーブルにダミーデータを作成しとくとイイかも。

```php:ダミーデータの作成
// Seederに記述（database/seeders/DatabaseSeeder.php）
          :
use App\Models\User;                // Userモデルを使うよー
class DatabaseSeeder extends Seeder {
  public function run() {
    User::factory(5)->create();    // ダミーデータを5つ作成
  }
}

// ダミーデータの作成実行（ターミナル）
% php artisan db:seed
```




## 【 Jetstream のロゴ変更 】
- ロゴを含んでるページは、ログインページ、新規登録ページ、ダッシュボードページなど。


|ビュー|デフォルト|変更後|
|:--:|:--:|:--:|
|ログインページ|<img src="https://gyazo.com/219a5a23338226feddea089e50cb1afe/raw">|<img src="https://gyazo.com/b38b9cc1407bce8bcf2e696824ddc32b/raw">|
|新規登録ページ|<img src="https://gyazo.com/feeaed754113f099674ce884584b8426/raw">|<img src="https://gyazo.com/6eb006caa58e0b85b00a627c7fdc40b9/raw">|
|ダッシュボードページ|<img src="https://gyazo.com/24fe71a87deae8586a3aec8963315dfd/raw">|<img src="https://gyazo.com/436ed418cbdf10280fb57d93312d8f0f/raw">|
|プロフィール情報更新ページ|<img src="https://gyazo.com/45e340f3b9464db86a31eef46cce23da/raw">|<img src="https://gyazo.com/21ad6a5f40d6717bff2e722f4d87334d/raw">|

- 今回は、FontAwesomeのアイコンを使うので、FontAwesome5を導入。ダウンロード or CDN リンクをビューファイルに貼りつける方法があるが、手っ取り早く、後者で。

```php:参）FontAwesomeの導入方法
// layout.blade.php の <head>タグに記述
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">
// 使い方
<i class="fas fa-map-pin"></i>
```
■ **ログイン、新規登録ページのロゴを変更する**

- layout ファイルは、resources/views/layouts/guest.blade.php 。
- ロゴ部分のコードは、Jetstreamのコンポーネントにある（resources/views/vendor/jetstream/components/authentication-card-logo.blade.php ）。
- それぞれ、resources/views/auth/login.blade.php と register.blade.php で読み込んで使ってるので、コンポーネント側の記述を変更。

```php:ログインページ、新規登録ページのロゴの変更
// FontAwesomeを使えるよーにする（resources/views/layouts/guest.blade.php の　<head>タグ内に記述）
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">

// FontAwesomeのアイコンに変更してみる（resources/views/vendor/jetstream/components/authentication-card-logo.blade.php）
<a href="/">
  <h1>Lunch Map</h1>
  <i class="fas fa-map-pin"></i>
</a>
```

■ **ダッシュボード、プロフィール情報更新ページのロゴを変更する**

- layout ファイルは、resources/views/layouts/app.blade.php 。
- ロゴ部分のコードは、Jetstreamのコンポーネントにある（resources/views/vendor/jetstream/components/authentication-card-logo.blade.php ）。
- それぞれのページのヘッダー部分でナビゲージョンバー（resources/views/navigation-dropdown.blade.php） を読み込んでる。さらに、ナビゲージョンバーで、Jetstreamのコンポーネントを読み込んでる。

```php:ダッシュボード、プロフィール情報更新ページのロゴの変更
// FontAwesomeを使えるよーにする（resources/views/layouts/app.blade.php の　<head>タグ内に記述）
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css">

// （方法1） コンポーネントのロゴ部分を変更（resources/views/vendor/jetstream/components/application-mark.blade.php）
<i class="fas fa-map-pin fa-2x"></i>
// ヘッダーのナビゲーション部分（resources/views/navigation-dropdown.blade.php）
<a href="{{ route('article.list') }}">   // リンク先も自分の設定したページに変更（※ デフォルトは、ダッシュボード）
  <x-jet-application-mark class="block h-9 w-auto" />
</a>
// （方法2） 若しくは、ナビゲーション部分を直接変更する（resources/views/navigation-dropdown.blade.php）
<a href="{{ route('article.list') }}">
  <i class="fas fa-map-pin fa-2x"></i>
</a>
```


## 【 アイコン写真の登録 】
- Jetstream導入時に、デフォルトで、usersテーブルに写真データ用のカラムが作成されてる。
- [Jeststream公式リファレンス](https://jetstream.laravel.com/1.x/features/profile-management.html#profile-photos)を参考に、config/jetstream.php で、該当箇所のコメントアウトを外し、アイコン写真を有効化。  
<img src="https://gyazo.com/c7b3e3c267a731f158e202b348cc337d/raw" width =80%>

```php:コメントアウトを外して有効化（config\jeststream.php）
'features' => [
  Features::profilePhotos(),
  Features::api(),
  Features::teams(),
],
```
- 写真データの保存場所は、 public\strage\profile-photos 。  
usersテーブルのprofile_photo_path カラムで、ココに格納されたデータを呼び出してる。

```php:保存場所へシンボリックリンクを貼る必要がある
% php artisan storage:link
```
「.env」ファイルのURLも環境に合わせて変更（※ 今回はローカル環境）。

```php:.envファイル
APP_URL=http://127.0.0.1:8000
```

#### （エラー） profile_photo_path カラムがないよ！
> jetstream sqlstate[42s22]: column not found: 1054 unknown column 'profile_photo_path' in 'field list'

- （状況）今回、アプリ実装途中で、Jetstreamを導入した。既にマイグレーションを何度か実行しており、usersテーブルが作成されてる状態だった。
  - Jetstream 導入によって、マイグレーションファイル（xxxxx_create_users_table.php）に、↓の記述が追加されるため、既に実行済みのマイグレーションファイルに追記されてしまい、カラムが作成できてなかったのが原因。

```php:xxxxx_create_users_table.php
// Jetstream 導入で、追加されたカラム
$table->foreignId('current_team_id')->nullable();
$table->text('profile_photo_path')->nullable();
```
- なので、マイグレーション 初期化 → 再実行で、profile_photo_path カラムが作成でき、解決。

```php:マイグレーションを初期化→再実行
% php artisan migrate:refresh
```