Laravel Breezeに画像のアップロード機能を追加する
はじめに
Laravel Breezeにユーザーのプロフィール画像のアップロード機能を追加していきます。
ユーザー認証機能はすでに実装しています。
環境
PHP 8.x
Laravel 10.x
MySQL 8.x
tl;dr
- プロフィール画像用カラムを追加する
- ビューを作成する
-
$fillable
プロパティにimage
カラムを追加する -
ProfileUpdateRequest
にバリデーションを追加する -
ProfileController
のupdateアクションを編集する - 画像を表示する
画像用カラムを追加する
ユーザーテーブルにプロフィール画像用カラムを追加します。
➜ php artisan make:migration add_image_to_users
INFO Migration [database/migrations/2023_10_30_141136_add_image_to_users.php] created successfully.
<?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::table('users', function (Blueprint $table) {
$table->string('image')->nullable();
});
}
};
➜ php artisan migrate
INFO Running migrations.
2023_10_30_141136_add_image_to_users ............................ 559ms DONE
アップロード用ビューを作成する
➜ php artisan make:component user-image --view
画像をアップロードする用ビューを作成します。
alpineJSを使ってアップロードされた画像のプレビューができるようにします。
また、画像がある場合画像を表示し、ない場合デフォルトのno-image.png
を表示するように設定しました。
<div x-data="imagePreview()">
<input @change="showPreview(event)" type="file" id="image" name="image">
<img src="{{ isset(Auth::user()->image) ? asset('storage/' . Auth::user()->image) : asset('images/no-image.png') }}" alt="" id="preview">
<script>
function imagePreview(){
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>
<x-input-label for="image" :value="__('Image')" />
<x-user-image />
<x-input-error class="mt-2" :messages="$errors->get('image')" />
</div>
画像をアップロードするために<form>
タグに enctype="multipart/form-data"
を追加します!
<form method="post" action="{{ route('profile.update') }}"
+ enctype="multipart/form-data" >
この部分がないと画像をアップロードできないので忘れないでくださいー
$fillable
プロパティにimage
を追加する
マスアサインメントの対策で$fillable
プロパティにimage
を追加します。
<?php
namespace App\Models;
...
class User extends Authenticatable
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
+ 'image',
];
}
バリデーションを追加する
ProfileUpdateRequest.php
にプロフィール画像のバリデーションを追加します。
<?php
...
class ProfileUpdateRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'name' => ['string', 'max:255'],
'email' => ['email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)],
+ 'image' => ['file', 'mimes:gif,png,jpg,webp', 'max:1024'],
];
}
}
ProfileControllerのupdateアクションを編集する
画像を保存するパスを指定します。
<?php
namespace App\Http\Controllers;
...
class ProfileController extends Controller
{
...
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->safe()->only(['name', 'email']));
// 画像があれば/uploadsに保存する、なければnull
$path = null;
if ($request->hasFile('image')) {
$path = $request->file('image')->store('uploads', 'public');
$request->user()->image = $path;
}
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'profile-updated');
}
...
}
画像のアップロード機能ができました。
public/uploads
にアップロードされたことを確認します。
➜ ls -n storage/app/public/uploads
total 80
-rw-r--r-- 1 501 20 40371 Oct 30 19:30 zxIGDT4sUjV8cwvciANZCQQ7sKLLLAaVeQVwXwVg.png
また、指定されたファイルタイプ以外のファイルをアップロードするとバリデーションエラーが表示されます。
新しい画像をアップロードされた場合、古い画像を削除する処理も追加していきます。
<?php
namespace App\Http\Controllers;
// Storageファサードをインポートする
use Illuminate\Support\Facades\Storage;
class ProfileController extends Controller
{
/**
* Update the user's profile information.
*/
public function update(ProfileUpdateRequest $request): RedirectResponse
{
if($currentImage = $request->user()->image){
Storage::disk('public')->delete($currentImage);
}
}
}
画像を表示する
➜ php artisan storage:link
INFO The [public/storage] link has been connected to [storage/app/public].
Laravelのアプリでphp artisan storage:link
コマンドを実行すると、通常、アプリケーションのパブリックディレクトリにあるファイルへのアクセスを簡単に設定するために使用されるシンボリックリンクが作成されます。
このようなリンクを作成することで、Webブラウザなどからアプリケーションのアップロードされたファイルにアクセスできるようになります。
public/storage
というディレクトリへのシンボリックリンク(シンボリックリンクはファイルシステム内の別の場所への参照のようなもので、リアルなデータは別の場所に格納されています)が、storage/app/public
というディレクトリに接続されました。
下記のコマンドで確認できます。
➜ ls -la public
リンクの参照する場所を変えたい場合、config/filesystems.php
を編集することができます。
<?php
return [
/*
|--------------------------------------------------------------------------
| Symbolic Links
|--------------------------------------------------------------------------
|
| Here you may configure the symbolic links that will be created when the
| `storage:link` Artisan command is executed. The array keys should be
| the locations of the links and the values should be their targets.
|
*/
'links' => [
public_path('storage') => storage_path('app/public'),
],
];
ナビゲーションにユーザーのプロフィール画像を表示させます。
<img src="{{ isset(Auth::user()->image) ? asset('storage/' . Auth::user()->image) : asset('images/no-image.png') }}">
終わりに
Laravel Breezeにユーザーのプロフィール画像をアップロードできるようになりましたー
Discussion