ブログ内検索

テック

Jetstream(Laravel)でプロフィール画像が表示されない会場はこちらです

jetstream-profile-image-cover.avif

どうも、毎回Jetstream使う度にこれで沼ってるこうじです。

いいかげん、毎回ここで時間使うわけにはいかないので、物理的にも重い腰を上げて備忘録したいと思います!

こうじ
こうじ
13kg太って、18kg痩せて、12kg太って、1kg痩せました。

今回の検証環境

今回はLaravelはインストールされている前提で進行します。
以下の環境で行なっています。参考までに...

名称 バージョン
Laravel 12.53.0
Jetstream 5.4.0
Livewire 3.7.11

Jetstreamをインストールしよう

まずはJetstreamをインストールします。
Composerでサクッといけます。

bash
composer require laravel/jetstream

パッケージを追加したらartisanコマンドでインストールします。

今回はベースの仕組みはLivewireを選択します。

bash
php artisan jetstream:install livewire

マイグレーションするかどうか聞かれるのでします。

DBが綺麗になるので、既存のLaravelの方は注意!
内部的にはphp artisan migrate:freshが走る場合があるので、新規じゃなくて不安な人はバックアップを取ってください。

bash
┌ New database migrations were added. Would you like to re-run your migrations? ┐
│ ● Yes / ○ No                                                                  │
└───────────────────────────────────────────────────────────────────────────────┘

Jetstreamのインストールが終わったらViteビルドの方も終わらせます。

bash
npm i && npm run build

これでJetstreamの準備は完了です!

こうじ
こうじ
ここまでは特に問題ないはず...

プロフィールページを確認

それではまずはプロフィール画面を確認してみましょう。
まずはユーザー登録を終わらせたら以下のような画面になります。

右上のプロフィールから移動
プロフィールページが表示されました

プロフィール画像が見当たらない場合

もしプロフィール画像の更新する箇所がない場合はconfig/jetstream.phpファイルの設定を確認してみてください。

Features::profilePhotos()のコメントアウトを外すことで表示されます。

config/jetstream.php php
'features' => [
    // Features::termsAndPrivacyPolicy(),
    Features::profilePhotos(), // <= コメントアウトを外す
    // Features::api(),
    // Features::teams(['invitations' => true]),
    Features::accountDeletion(),
],

configなので、一応キャッシュクリア

bash
php artisan config:clear

これで表示されると思います!

プロフィール画像を更新してみる

それではプロフィール画像を更新してみましょう。

こうじ
こうじ
俺は毎回ここでコケる
画像を変更します
無事画像が表示されました

しかしページを更新してみると・・・

更新すると画像が保存されていない!

一見問題なさそうに見えましたが、実際には画像が保存されていない!と言う事があります。

なんで表示されないの?

アップロード後にプレビューが見えているのは、Livewireの Temporary Uploads 機能でstorage/app/livewire-tmp に一時保存された画像を見ているからです。
なので、この段階ではまだ保存が確定しておりません。

保存ボタンを押すと、実際にDBの更新や本来の保存先へ画像が保存されます。
保存ボタンを押した後に表示されないのは、

  • 『一時保存パスから本番保存のパスへの移動』

  • 『DBへのパス書き込み』

このどちらかが失敗しているということになります。

保存しても保存されない場合は、ブラウザのデベロッパーツールのネットワークタブで保存時のレスポンスを見てみるのも良いかもしれません。

もし422 (Unprocessable Entity)とかになってたら、「バリデーションで弾かれてるな。」などの目安になります。

こうじ
こうじ
これが今回の記事の趣旨になります。

なんかこれ、普通にやってたらならないのかどうなのかわからないですけど、Livewireの公式にも特に言及がなくて余計沼った記憶があります。

画像が保存されていない

表示する為に確認すること

まずは自分の経験を元に一つずつチェックして行きます!

シンボリックリンクを確認

「実は画像もちゃんと保存されてるのに画像が表示されないよ!」っていう人はこれで解決できると思います。

Laravelは画像をstorageディレクトリに保存しますが、storageディレクトリはウェブ上からみることができません。

なのでstorageディレクトリを見れるようにする為、シンボリックリンクを貼り、publicディレクトリから表示できるようにします。

bash
php artisan storage:link

ちなみに、すでにシンボリックリンクが貼られている場合はエラーメッセージが出ますが、これは正常にシンボリックリンクが機能しているという事なので問題ありません。

bash
ERROR  The [public/storage] link already exists.

TrustProxiesの設定

リバースプロキシ環境やなど、特定のプロキシ環境を使用している場合など、自分以外のドメインに対して通信がうまくいかない場合があります。

その場合はTrustProxiesの設定を行うことで通信ができるようになります。

bootstrap/app.php php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->trustProxies(at: '*');
})

セキュリティ的な話

この設定は、要は「すべてのプロキシを許可しますよ〜」という意味ですが、セキュリティ的にはしっかりと必要なプロキシで絞って設定するのが正解です。

ただ、原因がどこにあるのかを探るためにまずはワイルドカード(全許可)する事でもし解決できたら「犯人はお前か!」と原因特定に繋がるので、まずは全部通してみるというというのはデバッグとしてはありだと思います。

モデルの$fillableを確認

$fillableにprofile_photo_pathのカラムが許可されていないと、Livewireがfillしたときにデータ自体が弾かれてしまいます。

保存する時に「安全なデータですよー。」というのを伝えるために$fillableにprofile_photo_pathを追加します。

app/Models/User.php php
protected $fillable = [
    'name',
    'email',
    'password',
    'profile_photo_path', // <= カラムを追加する
];

補足

本来であれば、JetstreamのデフォルトとしてはUpdateUserProfileInformationアクションでforceFill()を使っているので$fillableに追加しなくても動くのが想定です。

もし独自のカスタマイズを行なっていたり、UpdateUserProfileInformation.phpをカスタマイズしたりしてる場合、上記の方法で解決ができるかもしれません。

vendor/laravel/jetstream/HasProfilePhoto.php 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

これらに適切な権限を与えます。

ディレクトリとファイルにグループまで読み書きできるようにする

bash
find storage bootstrap/cache -type f -exec chmod 664 {} +
bash
find storage bootstrap/cache -type d -exec chmod 2775 {} +

「775」ではなく「2775」とすることで、親ディレクトリと同じグループ所有権を自動的に継承するようになります。

これをしておけば、「WEBサーバーのユーザーが作ったファイル」と「ユーザー(自分)がコマンドで叩いて作ったファイル」でグループがバラバラになって、またパーミッションエラーで沼る、なんて事を防げます。

その他の確認すべき項目

大概うまくいかない場合は、何かしらのエラーが残っていることが多いです。

  • storage/logs/laravel.logなどのログファイル

  • 各ブラウザのデベロッパーツールでのコンソールログやネットワークログ

  • storage/app/public/に画像が書き込まれているか

  • usersテーブルのprofile_photo_pathに値が書き込まれているか

こういうところから原因を切り分けてそもそもどこで問題が発生したかを検証すると、意外とサクッと解決できたりします。

僕は今回は解決できたので、また沼ったら更新しようと思います。

こうじ
こうじ
でもLivewire関連って静かなエラーが多いから意外とハマると時間溶けるんだよね(個人的感想)

まとめ

ほんとは、起きたら事案として備忘録しておけば後で困らないんだけど、時間がなくて「解決した!次!」ってなりがちで、また同じことやることが多いこうじです。

バージョンによっては、すんなりいったりいかなかったりすると思うけど、もしJetstream(Livewiew)関連で「画像がうまくいかないぞ!?」ってなった時のお供にどうぞ^^

関連記事

timestamps-converter-cover.avif

「このUNIXタイムスタンプいつ?」をサクッと解決!Mac開発者のための快適設定

Python+シェルスクリプトでUNIXタイムスタンプ(秒/ミリ秒)を即変換。

apple-emacs-shortcut-cover.avif

Macユーザーに捧ぐ!日常が変わるEmacs風ショートカット厳選まとめ

Macの標準機能で利用できるEmacsキーバインドを使って日常のテキスト入力などを効率化!

adobe-graduation-ceremony-cover.avif

【Adobe卒業式】年間10万円の節約!AdobeからApple Creator Studioに乗り換えた話

高額なAdobe税から卒業!解約の違約金トラップや、解約前にやるべき事などを解説。

vim-believer-cover.avif

爆速コーディングを実現するVimを布教する記事【入門編】

俺選!実務で爆速化する神コマンドを厳選して布教します。

timemachine-to-mac-transfer-cover.avif

意外と簡単!Time MachineでMac環境をお引越し(M1→M5)

Time MachineでMacを移行したらほぼそのまま復元できた話。

vite-first-guide-cover.avif

「アナタ無しでは生きられない!」Viteを使って今スグ爆速の開発環境を整えよ!

保存した瞬間にブラウザが更新されるViteの「ホットリロード」を活用した爆速開発環境を作る手順を解説!

wordpress-enqueue-scripts-cover.avif

コピペで完了!WordPressでスクリプトにdefer・async・moduleを付ける方法

WordPressでスクリプトにdefer・async・moduleを付ける方法を解説。コピペで使えるサンプル付き。

cloud-document-cover.avif

使ってみたいよね?Adobeクラウドドキュメントを試してみた

Adobeクラウドドキュメントを試して感じたメリットとデメリットをまとめました。

lv-sl-error-cover.avif

Laravelでstorageの画像が表示されない件

Webアプリ(Laravel)をサーバー移動した際に画像が表示されなかった場合の備忘録です。

railx6sj-cover.avif

ssh接続エラー: Too many authentication failuresをどうにかする

SSH接続時に出現する「Too many authentication failures」のエラーの解決法についてご紹介。

kusanagi-sever-error-cover.avif

KUSANAGIでサイトが表示されない場合は初心にかえる

KUSANAGIサーバーでどうやっても希望のドメインがサイトから見れなかった場合に試してたい選択肢をご紹介。

kusanagi-laravel-cover.avif

「爆速KUSANAGI」に『爆速Laravel』をインストール

爆速WordPressで人気の高いKUSANAGIサーバーに、Laravelをインストールし、爆速で使って見ようという事になりました。


新着記事一覧

kindle-comic.png

無料で漫画を一気読み!Kindleで出産レポ・育児漫画などなど

timestamps-converter-cover.avif

「このUNIXタイムスタンプいつ?」をサクッと解決!Mac開発者のための快適設定

Python+シェルスクリプトでUNIXタイムスタンプ(秒/ミリ秒)を即変換。

apple-emacs-shortcut-cover.avif

Macユーザーに捧ぐ!日常が変わるEmacs風ショートカット厳選まとめ

Macの標準機能で利用できるEmacsキーバインドを使って日常のテキスト入力などを効率化!

adobe-graduation-ceremony-cover.avif

【Adobe卒業式】年間10万円の節約!AdobeからApple Creator Studioに乗り換えた話

高額なAdobe税から卒業!解約の違約金トラップや、解約前にやるべき事などを解説。

vim-believer-cover.avif

爆速コーディングを実現するVimを布教する記事【入門編】

俺選!実務で爆速化する神コマンドを厳選して布教します。

timemachine-to-mac-transfer-cover.avif

意外と簡単!Time MachineでMac環境をお引越し(M1→M5)

Time MachineでMacを移行したらほぼそのまま復元できた話。

apple-fitness-plus-cover.avif

体重乱高下アラフォーがApple Fitness+に行き着いた話

激しい体重の増減を繰り返したチョコザップユーザーが、コスパ最強の「Apple Fitness+」に乗り換えました。

vite-first-guide-cover.avif

「アナタ無しでは生きられない!」Viteを使って今スグ爆速の開発環境を整えよ!

保存した瞬間にブラウザが更新されるViteの「ホットリロード」を活用した爆速開発環境を作る手順を解説!

with-pomodoro-technique-cover.avif

「私のコトですか?」過集中&注意力散漫な人に刺さる!効率化ではなく“生存戦略”のポモドーロ術

集中力が「0か200」しかない人に朗報!ポモドーロ・テクニックが脳の暴走を止める最強の特効薬でした。

wordpress-enqueue-scripts-cover.avif

コピペで完了!WordPressでスクリプトにdefer・async・moduleを付ける方法

WordPressでスクリプトにdefer・async・moduleを付ける方法を解説。コピペで使えるサンプル付き。

cloud-document-cover.avif

使ってみたいよね?Adobeクラウドドキュメントを試してみた

Adobeクラウドドキュメントを試して感じたメリットとデメリットをまとめました。

lv-sl-error-cover.avif

Laravelでstorageの画像が表示されない件

Webアプリ(Laravel)をサーバー移動した際に画像が表示されなかった場合の備忘録です。