Jetstream(Laravel)でプロフィール画像が表示されない会場はこちらです
どうも、毎回Jetstream使う度にこれで沼ってるこうじです。
いいかげん、毎回ここで時間使うわけにはいかないので、物理的にも重い腰を上げて備忘録したいと思います!
今回の検証環境
今回はLaravelはインストールされている前提で進行します。
以下の環境で行なっています。参考までに...
| 名称 | バージョン |
|---|---|
| Laravel | 12.53.0 |
| Jetstream | 5.4.0 |
| Livewire | 3.7.11 |
Jetstreamをインストールしよう
まずはJetstreamをインストールします。
Composerでサクッといけます。
composer require laravel/jetstream
パッケージを追加したらartisanコマンドでインストールします。
今回はベースの仕組みはLivewireを選択します。
php artisan jetstream:install livewire
マイグレーションするかどうか聞かれるのでします。
DBが綺麗になるので、既存のLaravelの方は注意!
内部的にはphp artisan migrate:freshが走る場合があるので、新規じゃなくて不安な人はバックアップを取ってください。
┌ New database migrations were added. Would you like to re-run your migrations? ┐
│ ● Yes / ○ No │
└───────────────────────────────────────────────────────────────────────────────┘
Jetstreamのインストールが終わったらViteビルドの方も終わらせます。
npm i && npm run build
これでJetstreamの準備は完了です!
プロフィールページを確認
それではまずはプロフィール画面を確認してみましょう。
まずはユーザー登録を終わらせたら以下のような画面になります。
プロフィール画像が見当たらない場合
もしプロフィール画像の更新する箇所がない場合はconfig/jetstream.phpファイルの設定を確認してみてください。
Features::profilePhotos()のコメントアウトを外すことで表示されます。
'features' => [
// Features::termsAndPrivacyPolicy(),
Features::profilePhotos(), // <= コメントアウトを外す
// Features::api(),
// Features::teams(['invitations' => true]),
Features::accountDeletion(),
],
configなので、一応キャッシュクリア
php artisan config:clear
これで表示されると思います!
プロフィール画像を更新してみる
それではプロフィール画像を更新してみましょう。
しかしページを更新してみると・・・
一見問題なさそうに見えましたが、実際には画像が保存されていない!と言う事があります。
なんで表示されないの?
アップロード後にプレビューが見えているのは、Livewireの Temporary Uploads 機能でstorage/app/livewire-tmp に一時保存された画像を見ているからです。
なので、この段階ではまだ保存が確定しておりません。
保存ボタンを押すと、実際にDBの更新や本来の保存先へ画像が保存されます。
保存ボタンを押した後に表示されないのは、
『一時保存パスから本番保存のパスへの移動』
『DBへのパス書き込み』
このどちらかが失敗しているということになります。
保存しても保存されない場合は、ブラウザのデベロッパーツールのネットワークタブで保存時のレスポンスを見てみるのも良いかもしれません。
もし422 (Unprocessable Entity)とかになってたら、「バリデーションで弾かれてるな。」などの目安になります。
なんかこれ、普通にやってたらならないのかどうなのかわからないですけど、Livewireの公式にも特に言及がなくて余計沼った記憶があります。
画像が保存されていない
表示する為に確認すること
まずは自分の経験を元に一つずつチェックして行きます!
シンボリックリンクを確認
「実は画像もちゃんと保存されてるのに画像が表示されないよ!」っていう人はこれで解決できると思います。
Laravelは画像をstorageディレクトリに保存しますが、storageディレクトリはウェブ上からみることができません。
なのでstorageディレクトリを見れるようにする為、シンボリックリンクを貼り、publicディレクトリから表示できるようにします。
php artisan storage:link
ちなみに、すでにシンボリックリンクが貼られている場合はエラーメッセージが出ますが、これは正常にシンボリックリンクが機能しているという事なので問題ありません。
ERROR The [public/storage] link already exists.
TrustProxiesの設定
リバースプロキシ環境やなど、特定のプロキシ環境を使用している場合など、自分以外のドメインに対して通信がうまくいかない場合があります。
その場合はTrustProxiesの設定を行うことで通信ができるようになります。
->withMiddleware(function (Middleware $middleware): void {
$middleware->trustProxies(at: '*');
})
セキュリティ的な話
この設定は、要は「すべてのプロキシを許可しますよ〜」という意味ですが、セキュリティ的にはしっかりと必要なプロキシで絞って設定するのが正解です。
ただ、原因がどこにあるのかを探るためにまずはワイルドカード(全許可)する事でもし解決できたら「犯人はお前か!」と原因特定に繋がるので、まずは全部通してみるというというのはデバッグとしてはありだと思います。
モデルの$fillableを確認
$fillableにprofile_photo_pathのカラムが許可されていないと、Livewireがfillしたときにデータ自体が弾かれてしまいます。
保存する時に「安全なデータですよー。」というのを伝えるために$fillableにprofile_photo_pathを追加します。
protected $fillable = [
'name',
'email',
'password',
'profile_photo_path', // <= カラムを追加する
];
補足
本来であれば、JetstreamのデフォルトとしてはUpdateUserProfileInformationアクションでforceFill()を使っているので$fillableに追加しなくても動くのが想定です。
もし独自のカスタマイズを行なっていたり、UpdateUserProfileInformation.phpをカスタマイズしたりしてる場合、上記の方法で解決ができるかもしれません。
public function updateProfilePhoto(UploadedFile $photo, $storagePath = 'profile-photos')
{
tap($this->profile_photo_path, function ($previous) use ($photo, $storagePath) {
$this->forceFill([
'profile_photo_path' => $photo->storePublicly(
$storagePath, ['disk' => $this->profilePhotoDisk()]
),
])->save();
if ($previous) {
Storage::disk($this->profilePhotoDisk())->delete($previous);
}
});
}
権限の確認
意外に忘れてて、そして時間を溶かす原因の一つはサーバー上のディレクトリ「権限」の問題です。
Laravelが画像を保存しようとしても、保存先のディレクトリに「書き込み権限」がないと、画像ファイル自体が生成されません。
「全て問題なさそうなのに何故か保存されないぞ!」みたいな事があったら、これが原因かもしれません。
一般的にサーバー側が触ってくるディレクトリは以下の二つです。
storage
bootstrap/cache
これらに適切な権限を与えます。
ディレクトリとファイルにグループまで読み書きできるようにする
find storage bootstrap/cache -type f -exec chmod 664 {} +
find storage bootstrap/cache -type d -exec chmod 2775 {} +
「775」ではなく「2775」とすることで、親ディレクトリと同じグループ所有権を自動的に継承するようになります。
これをしておけば、「WEBサーバーのユーザーが作ったファイル」と「ユーザー(自分)がコマンドで叩いて作ったファイル」でグループがバラバラになって、またパーミッションエラーで沼る、なんて事を防げます。
その他の確認すべき項目
大概うまくいかない場合は、何かしらのエラーが残っていることが多いです。
storage/logs/laravel.logなどのログファイル
各ブラウザのデベロッパーツールでのコンソールログやネットワークログ
storage/app/public/に画像が書き込まれているか
usersテーブルのprofile_photo_pathに値が書き込まれているか
こういうところから原因を切り分けてそもそもどこで問題が発生したかを検証すると、意外とサクッと解決できたりします。
僕は今回は解決できたので、また沼ったら更新しようと思います。
まとめ
ほんとは、起きたら事案として備忘録しておけば後で困らないんだけど、時間がなくて「解決した!次!」ってなりがちで、また同じことやることが多いこうじです。
バージョンによっては、すんなりいったりいかなかったりすると思うけど、もしJetstream(Livewiew)関連で「画像がうまくいかないぞ!?」ってなった時のお供にどうぞ^^