【Laravel】Vue + Inertiaで画像アップロードする方法

laravelアイキャッチ JavaScript

こんにちは、かつコーチです。

今回は、LaravelのVuejs + Inertiaを使った画像のアップロード方法についてです。

コード全体

Index.vueで一覧画面と登録画面を一緒にしています。

<script setup>
import { ref } from "vue";
import { Head, useForm } from "@inertiajs/vue3";
// Layouts
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout.vue";
// Components
import InputError from "@/Components/InputError.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";

defineProps({
  images: {
    type: Array,
    required: true,
  },
});

const form = useForm({
  image: null,
});

const previewImage = ref(null);

const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      previewImage.value = URL.createObjectURL(file);
      form.image = file;
    } else {
      previewImage.value = null;
      form.image = null;
    }
};

const uploadImage = () => {
    form.post(route('image.store'), {
        onSuccess: () => {
            form.reset('image');
            previewImage.value = null;
        },
    });
};
</script>

<template>
  <Head title="Profile" />

  <AuthenticatedLayout>
    <template #header>
      <div class="flex justify-between items-center">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">Image</h2>
      </div>
    </template>

    <div class="flex gap-5 p-6">
      <!-- 画像一覧 -->
      <div class="flex flex-col">
        <h3>画像一覧</h3>
        <div v-if="images.length" class="grid grid-cols-4 gap-5">
          <div v-for="image in images" :key="image.id" class="border rounded">
            <img
              :src="`/storage/images/${image.path}`"
              alt="Uploaded Image"
              class="w-full h-40 object-cover"
            />
          </div>
        </div>
        <div v-else>
          <p>画像がありません</p>
        </div>
      </div>
      <div class="w-80 flex flex-col">
        <h2>作成</h2>
        <form @submit.prevent="uploadImage">
          <div class="mb-4">
            <label
              for="image"
              class="block text-sm font-medium text-gray-700"
              @change="form.image = $event.target.files[0]"
              >Image</label
            >
            <input
              type="file"
              name="image"
              id="image"
              class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"
              @change="handleFileChange"
            />
            <InputError :message="form.errors.password" class="mt-2" />
          </div>
          <div v-if="previewImage" class="mb-4">
            <h3>プレビュー画面</h3>
            <img :src="previewImage"
              alt="Preview Image"
              class="w-full h-40 object-cover"
            />

          </div>
          <PrimaryButton type="submit" :disabled="form.processing"
            >登録</PrimaryButton
          >
        </form>
      </div>
    </div>
  </AuthenticatedLayout>
</template>

一覧画面の部分

      <div class="flex flex-col">
        <h3>画像一覧</h3>
        <div v-if="images.length" class="grid grid-cols-4 gap-5">
          <div v-for="image in images" :key="image.id" class="border rounded">
            <img
              :src="`/storage/images/${image.path}`"
              alt="Uploaded Image"
              class="w-full h-40 object-cover"
            />
          </div>
        </div>
        <div v-else>
          <p>画像がありません</p>
        </div>
      </div>

画像があれば表示し、ない場合は「画像がありません。」と表示します。

登録画面の部分

      <div class="w-80 flex flex-col">
        <h2>登録</h2>
        <form @submit.prevent="uploadImage">
          <div class="mb-4">
            <label
              for="image"
              class="block text-sm font-medium text-gray-700"
              @change="form.image = $event.target.files[0]"
              >Image</label
            >
            <input
              type="file"
              name="image"
              id="image"
              class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md"
              @change="handleFileChange"
            />
            <InputError :message="form.errors.password" class="mt-2" />
          </div>
          <div v-if="previewImage" class="mb-4">
            <h3>プレビュー画面</h3>
            <img :src="previewImage"
              alt="Preview Image"
              class="w-full h-40 object-cover"
            />

          </div>
          <PrimaryButton type="submit" :disabled="form.processing"
            >登録</PrimaryButton
          >
        </form>
      </div>

次に、Script部分です。

import { ref } from "vue";
import { Head, useForm } from "@inertiajs/vue3";
// Layouts
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout.vue";
// Components
import InputError from "@/Components/InputError.vue";
import PrimaryButton from "@/Components/PrimaryButton.vue";

defineProps({
  images: {
    type: Array,
    required: true,
  },
});

const form = useForm({
  image: null,
});

const previewImage = ref(null);

const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      previewImage.value = URL.createObjectURL(file);
      form.image = file;
    } else {
      previewImage.value = null;
      form.image = null;
    }
};

const uploadImage = () => {
    form.post(route('image.store'), {
        onSuccess: () => {
            form.reset('image');
            previewImage.value = null;
        },
    });
};

コントローラーは次の通りです。

    public function store(StoreImageRequest $request)
    {
        $request->validated();

        $file = $request->file('image');

        $filename = $file->hashName();

        $path = $file->storeAs('images', $filename, 'public');
        $filename = basename($path);

        try {
            DB::transaction(function () use ($filename) {
                Image::create([
                    'path' => $filename,
                ]);
            });

            return to_route('images.index')->with('success', 'Image uploaded successfully.');
        } catch (\Throwable $e) {
            Log::error($e->getMessage());

            return redirect()->back()->with('error', 'An error occurred while uploading the image.');
        }
    }
タイトルとURLをコピーしました