LaravelのBreezeを使うと簡単に認証機能を追加できます。
name、email、passwordからなるシンプルなプロフィール。
ここに、プロフィール画像を投稿する機能を追加してみましょう!
テーブルを変更する
まず、User テーブルに 画像の名前 を保存するカラムを追加します。
マイグレーションファイルを編集し、以下のように image_name を追加します。
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('image_name')->nullable(); // これ
$table->rememberToken();
$table->timestamps();
});モデルを変更する
app/Models/User.php に image_name を追加します。
protected $fillable = [
'name',
'email',
'password',
'image_name', // これ
];リクエストを変更する
app/Http/Requests/ProfileUpdateRequest.php にも image_name を追加します。
これによって、ファイルの拡張子や、データのサイズを指定できます。
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:255'],
'image' => ['file', 'mimes:gif,png,jpg,webp', 'max:3072'], // これ
];
}画像投稿フォームを作る
Pages/Profile/Partials/UpdateProfileInformationForm.tsxのフォームの中に、画像ファイル選択用のinputと、プレビュー表示用のimgを追加します。
inputにはtype="file"を指定します。
なお、ファイルの送信を行う際には、formにはencType="multipart/form-data"を指定します。
<form
onSubmit={submit}
className="mt-6 space-y-6"
encType="multipart/form-data"
>
<div>
<InputLabel htmlFor="image" value="Image" />
<div className="flex items-center gap-4 pt-2">
<img
src={previewImage}
alt="プレビュー画像"
className="w-16 h-16 rounded-full object-cover border-none"
></img>
<input
type="file"
id="image"
name="image"
onChange={handleImageChange}
/>
</div>
</div>プレビュー画像の表示とハンドリング
プレビュー画像用のstateと、handleImageChange関数を用意します。
const { data, setData, post, errors, processing, recentlySuccessful } =
useForm<any>({
name: user.name,
email: user.email,
_method: "patch", // 追加
});
const [previewImage, setPreviewImage] = useState(
user.image_name
? `/storage/${user.image_name}`
: "/images/default_image.png"
);
const submit: FormEventHandler = (e) => {
e.preventDefault();
post(route("profile.update")); // patchからpostに変更
};
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const imageUrl = URL.createObjectURL(file);
setPreviewImage(imageUrl); // 選択された画像のプレビューを表示
setData("image", file); // 画像データをセット
} else {
setData("image", null); // 画像が選択されていない場合にnullを設定
}
};プレビュー画像用のstateには登録済みの画像のパスを入れます。
未登録であればpublicフォルダに入れたデフォルト画像のパスを入れます。
URL.createObjectURL(file)で選択された画像の一時的なURLを生成し、プレビューに使用します。
Submitの処理
フォーム送信時のsubmit関数のメソッドは、デフォルトではpatchなっているのですが、multipart/form-dataを使用するためpostに変更します。
useFormに_method: "patch"を加えることで、POSTリクエスト内でもPATCH処理が行われます。
もともとHTMLの仕様でPOSTかGETしか指定ません。
Inetiaのおかげでどのメソッドも柔軟に扱えるようになっていました。
Inetiaってすごいね
formにencType="multipart/form-data"を指定する場合はInetiaのこの機能が働かなくなるので、postで代用する必要があります。
コントローラーを修正する
app/Http/Controllers/ProfileController.php を下記のように修正します。
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->safe()->only(['name', 'email']));
if ($request->hasFile('image')) {
$path = $request->file('image')->store('images', 'public');
$request->user()->image_name = $path;
}
$request->user()->save();
return Redirect::route('profile.edit')->with('status', 'Profile updated successfully!');
}リクエストにファイルが含まれている場合、そのファイルをstoreメソッドでpublic/images フォルダに保存します。
保存先のパスはDBのimage_nameに保存します。
完成した

Breezeは基本的な認証機能をサクッと実装できるし、カスタマイズもしやすいのが魅力ですね!
