Laravel Breeze に「プロフィールアイコン画像」機能を追加する
本来、プロフィールアイコンの機能を使用する場合は Laravel の別の認証パッケージである「Laravel Jetstream」を使用すれば良いのですが、そこまで多機能な認証機能を使用しない場合、Breeze の方でも意外と簡単に実装できたので備忘録として記事にしたいと思います。
プロフィール画像を追加する手順
Step 1, Laravel & Breeze のインストール
まずは 素のLaravel と Laravel Breeze をインストールするところから。Laravel をインストールするフォルダに移動して Laravel をインストールします(今回は Laravel のインストーラでインストールしています)。
laravel new project_name
インストールした Laravel のプロジェクトルートに移動します。
cd project_name
Laravel Breeze をインストール。
composer require laravel/breeze --dev
認証に必要なリソースを公開して、そのまま続けて必要な依存パッケージを npm からダウンロードしてフロント環境をビルドします。
php artisan breeze:install
マイグレーション処理を行います。
php artisan migrate
これで Breeze の認証機能を使用できるようになりました!ここで一度ブラウザで認証機能が正常に動作するかを確かめると良いでしょう。
Step 2, プロフィールページの編集
resources\views\profile\partials\update-profile-information-form.blade.php
の20行目あたりに、画像をアップロードするコンポーネントを表示するための x-picture-input
タグ及びエラー時の x-input-error
タグを追加します。また、form タグにも画像をアップロードするために enctype="multipart/form-data"
を追記します。
// ...
+ <form method="post" action="{{ route('profile.update') }}" class="mt-6 space-y-6" enctype="multipart/form-data">
@csrf
@method('patch')
+ <div>
+ <x-picture-input />
+ <x-input-error class="mt-2" :messages="$errors->get('picture')" />
+ </div>
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)" required autofocus autocomplete="name" />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
// ...
x-picture-input
タグに対応する Blade コンポーネントを作成します。以下のようにコマンドで作成しましょう。
php artisan make:component picture-input --view
すると resources\views\components\picture-input.blade.php
という新しいファイルが作成されますので、このファイルを以下のように編集します。
<div class="flex mb-4" x-data="picturePreview()">
<div class="mr-3">
<img
id="preview"
src="{{ isset(Auth::user()->profile_photo_path) ? asset('storage/' . Auth::user()->profile_photo_path) : asset('images/user_icon.png') }}"
alt=""
class="w-16 h-16 rounded-full object-cover border-none bg-gray-200">
</div>
<div class="flex items-center">
<button
x-on:click="document.getElementById('picture').click()"
type="button"
class="inline-flex items-center uppercase rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
SELECT A NEW PHOTO
</button>
<input @change="showPreview(event)" type="file" name="picture" id="picture" class="hidden">
<script>
function picturePreview() {
return {
showPreview: (event) =>{
if(event.target.files.length > 0){
var src = URL.createObjectURL(event.target.files[0]);
document.getElementById('preview').src = src;
}
}
}
}
</script>
</div>
</div>
上記を記入して保存したら、あなた好みのユーザーアイコンの素材をDLして public/images
フォルダ内に入れます。上記の img タグにある src
属性のURLに則って画像のファイル名は user_icon.png
としています。今回のサンプルでは、こちらのサイト からユーザーアイコンを DL しました。アイコン画像を保存したフォルダ階層は以下のような感じです。
public/
├ images/
│ └ user_icon.png
├ ...
今の状態でブラウザで表示を確認してみると、うまく表示できていないと思います。そのときは npm run build
でフロント周りのビルド作業を行ってください。
npm run build
そうするとうまくブラウザで表示できるようになったと思います。
Step 3, User モデルとマイグレーションファイルの編集
uesrs テーブルのマイグレーションファイルである database\migrations\2014_10_12_000000_create_users_table.php
を編集して、プロフィールアイコンのパスを保存するカラムを追加します。profile_photo_path
カラムを追加する記述 1行を追加します。
// ...
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
+ $table->string('profile_photo_path')->nullable(); // この行を追加
$table->rememberToken();
$table->timestamps();
});
}
//...
profile_photo_path
カラムにデータを登録できるようにするために User モデルの app\Models\User.php
ファイル protected $fillable
の部分を以下のように編集します。
// ...
protected $fillable = [
'name',
'email',
'password',
+ 'profile_photo_path', // この行を追加
];
// ...
ここで一度、マイグレート処理を最初からやり直してみましょう。
php artisan migrate:fresh
こうすることで users テーブルに profile_photo_path
列が新たに登録されました。(登録されていたユーザーは初期化されてしまうので再度ユーザー情報を登録し直してください)
Step 4, アイコン画像を登録する処理を記述する
まず、アップロードされる画像のバリデーション設定を app\Http\Requests\ProfileUpdateRequest.php
に追記します。
// ...
public function rules(): array
{
return [
'name' => ['string', 'max:255'],
'email' => ['email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)],
+ 'picture' => ['file', 'mimes:gif,png,jpg,webp', 'max:3072'],
];
}
// ...
ユーザーアイコンを保存するための処理を app\Http\Controllers\ProfileController.php
の update
アクションに記述します。以下のように編集します。
// ...
public function update(ProfileUpdateRequest $request): RedirectResponse
{
- $request->user()->fill($request->validated());
+ $request->user()->fill($request->safe()->only(['name', 'email']));
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
}
+ $path = null;
+ if ($request->hasFile('picture')) {
+ $path = $request->file('picture')->store('profile-icons', 'public');
+ $request->user()->profile_photo_path = $path;
+ }
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
// ...
アップロードされた画像を表示するため public/storage
から storage/app/public
へのシンボリックリンクを張ります。以下のコマンドを実行します。
php artisan storage:link
ブラウザで確認してみましょう!
お疲れさまでした!これでプロフィールページから画像のアップロード処理を行うことができるようになっているかと思います。もう一度、会員登録をやり直してプロフィールページ上でアップロードを正常に行うことができるかご確認ください。