実現したいこと
Laravel + Vue.js 3 + Inertia で開発中のお知らせ機能に複数画像アップロードがあります。
登録済みの複数画像をお知らせ編集画面に表示出来ずに困っております。
前提
Laravel、Vue.js3、Inertia いずれも初心者です。
お知らせ投稿の項目は、
・タイトル
・ファイル(複数)
です。(項目は他にもありますが、ここでの質問には不要なので省略しています)
複数ファイルのアップロードに「filepond」ライブラリを使用することにしました。
また、Laravelで使いやすくなると思い、併せて「laravel-filepond」も使用することにしました。
DBは、newsテーブルとimagesテーブル、その中間テーブルがあります。
news --------- id title images --------- id filepath image_news --------- news_id image_id
発生している問題・エラーメッセージ
Create→Storeは成功したのですが、EditでFilepondを適用したInput[type=file]に登録済みの画像をセットすることが出来ずに困っています。
以下の画像のようなイメージです。
(各画像の右上「Upload complete」表示は不要で大丈夫です)
該当のソースコード
文字数オーバーのため省略部分あります
Create.vue
javascript
1<script setup> 2import { Head, useForm } from '@inertiajs/inertia-vue3'; 3import { onMounted, ref } from "vue"; 4import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'; 5import ValidationErrors from '@/Components/ValidationErrors.vue'; 6import vueFilePond, { setOptions } from 'vue-filepond'; 7import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'; 8import FilePondPluginImagePreview from 'filepond-plugin-image-preview'; 9 10// Import filepond styles 11import 'filepond/dist/filepond.min.css'; 12import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'; 13 14const props = defineProps({ 15 csrf_token: String 16}) 17 18const filepondImagesInput = ref(null); // Reference the input to clear the files later 19 20const form = useForm({ 21 title: null, 22 images: [], 23}) 24 25const storeNews = () => { 26 form.transform((data) => { 27 return { 28 ...data, 29 images: data.images.map(item => item.serverId) // Pluck only the serverIds 30 } 31 }) 32 form.post(route('news.store'), { 33 forceFormData: true, 34 onSuccess: () => { 35 filepondImagesInput.value.removeFiles(); 36 } 37 }) 38} 39 40// Create FilePond component 41const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview); 42 43// Set global options on filepond init 44const handleFilePondInit = () => { 45 setOptions({ 46 credits: false, 47 server: { 48 url: '/filepond', 49 headers: { 50 'X-CSRF-TOKEN': props.csrf_token, 51 } 52 } 53 }); 54}; 55 56// Set the server id from response 57const handleFilePondImagesProcess = (error, file) => { 58 form.images.push({id: file.id, serverId: file.serverId}); 59}; 60// Remove the server id on file remove 61const handleFilePondImagesRemoveFile = (error, file) => { 62 form.images = form.images.filter(item => item.id !== file.id); 63} 64</script> 65 66<template> 67 <Head title="新規登録" /> 68 69 <AuthenticatedLayout> 70 <template #header> 71 <h2 class="font-bold text-2xl text-stone-800 leading-tight">新規登録</h2> 72 </template> 73 74 <ValidationErrors></ValidationErrors> 75 76 <section class="text-stone-800 body-font relative"> 77 <form @submit.prevent="storeNews" enctype="multipart/form-data"> 78 <div class="pt-10 pb-24"> 79 <div class="flex flex-wrap -m-2"> 80 <div class="p-2 w-full"> 81 <label for="title" class="form-label">タイトル<span class="badge-danger">必須</span></label> 82 <input type="text" id="title" name="title" v-model="form.title" class="w-full form-ctrl"> 83 <p class="form-attention">100文字以内で入力してください</p> 84 </div> 85 <div class="p-2 w-full"> 86 <label class="block form-label">添付ファイル</label> 87 <p class="form-attention">2MB以下のjpg/pngファイルを指定してください</p> 88 <div> 89 <FilePond 90 name="images" 91 ref="filepondImagesInput" 92 class-name="my-pond" 93 allow-multiple="true" 94 accepted-file-types="image/jpeg, image/png" 95 max-files="3" 96 @init="handleFilePondInit" 97 @processfile="handleFilePondImagesProcess" 98 @removefile="handleFilePondImagesRemoveFile" 99 /> 100 </div> 101 </div> 102 <div class="px-2 pt-10 w-full"> 103 <button class="btn-primary">登録</button> 104 </div> 105 </div> 106 </div> 107 </form> 108 </section> 109 110 </AuthenticatedLayout> 111</template>
Edit.vue
javascript
1<script setup> 2// importはCreateと同じため省略 3 4const props = defineProps({ 5 news: Object, 6 images: Array, 7 csrf_token: String 8}) 9 10const filepondImagesInput = ref(null); // Reference the input to clear the files later 11 12const form = useForm({ 13 _method: 'PUT', 14 id: props.news.id, 15 title: props.news.title, 16 images: props.images, 17}) 18 19onMounted(() => { 20 form.images.push(props.images); // ←試しにやってみましたが上手くいかず 21}) 22 23const updateNews = id => { 24 form.transform((data) => { 25 return { 26 ...data, 27 images: data.images.map(item => item.serverId) // Pluck only the serverIds 28 } 29 }) 30 form.post(route('news.update', { news: id }), { 31 forceFormData: true, 32 onSuccess: () => { 33 filepondImagesInput.value.removeFiles(); 34 } 35 }) 36} 37 38// Create FilePond component 39// Createの40行目〜63行目まで同じため省略 40</script> 41 42<template> 43 Createと同じ 44</template>
NewsController.php
php
1<?php 2 3namespace App\Http\Controllers; 4 5use App\Http\Requests\StoreNewsRequest; 6use App\Http\Requests\UpdateNewsRequest; 7use App\Models\News; 8use App\Models\Image; 9use Illuminate\Support\Facades\Auth; 10use Illuminate\Support\Facades\DB; 11use Illuminate\Support\Facades\Storage; 12use Inertia\Inertia; 13use RahulHaque\Filepond\Facades\Filepond; 14 15class NewsController extends Controller 16{ 17 public function index() 18 { 19 // 省略 20 } 21 22 public function create() 23 { 24 return Inertia::render('News/Create'); 25 } 26 27 public function store(StoreNewsRequest $request) 28 { 29 DB::beginTransaction(); 30 31 try { 32 // 添付ファイル 33 $news_images = Filepond::field($request->images)->getFile(); 34 $image_ids = array(); 35 if ( isset($news_images) ) { 36 foreach ( $news_images as $n_img ) { 37 $filepath = $n_img->store('news', 'public'); 38 $img = Image::create([ 39 'title' => $n_img->getClientOriginalName(), 40 'filepath' => $filepath, 41 ]); 42 $image_ids[] = $img->id; 43 } 44 } 45 46 $news = News::create([ 47 'title' => $request->title, 48 ]); 49 50 $news->images()->sync($image_ids); 51 52 DB::commit(); 53 54 return to_route('news.index')->with([ 55 'message' => '登録しました。', 56 'status' => 'success' 57 ]); 58 59 } catch (\Exception $e) { 60 DB::rollBack(); 61 } 62 } 63 64 public function show(News $news) 65 { 66 // 省略 67 } 68 69 public function edit(News $news) 70 { 71 $images = Image::select('id', 'filepath')->orderBy('id')->get(); 72 73 return Inertia::render('News/Edit', [ 74 'news' => $news, 75 'images' => $images, 76 ]); 77 } 78 79 public function update(UpdateNewsRequest $request, News $news) 80 { 81 DB::beginTransaction(); 82 try { 83 $news_images = Filepond::field($request->images)->getFile(); 84 $image_ids = array(); 85 if ( isset($news_images) ) { 86 foreach ( $news_images as $n_img ) { 87 $filepath = $n_img->store('news', 'public'); 88 $img = Image::create([ 89 'title' => $n_img->getClientOriginalName(), 90 'filepath' => $filepath, 91 ]); 92 $image_ids[] = $img->id; 93 } 94 } 95 96 $news->title = $request->title; 97 $news->save(); 98 99 $news->images()->sync($image_ids); 100 101 DB::commit(); 102 103 return to_route('news.index')->with([ 104 'message' => '更新しました。', 105 'status' => 'success' 106 ]); 107 108 } catch (\Exception $e) { 109 DB::rollBack(); 110 } 111 } 112 113 public function destroy(News $news) 114 { 115 // 116 } 117}
試したこと
英語はさっぱりですが、藁にもすがる気持ちで以下のYouTubeをみました。
Laravel Inertia Vue.js - Multiple image upload with FilePond.js
私がやりたいことは、実現は出来るだろうと思いましたが、この動画ではlaravel-filepondが使用されていないのでアプローチが違うと思いました。
laravel-filepondの使用をあきらめた方が早いのでしょうか???
補足情報(FW/ツールのバージョンなど)
Laravel 9
Vue.js 3
Inertia
filepond
laravel-filepond

回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。