intertia.js + filepond + spatie media lib決定版(3) 編集時のファイル処理およびfilepondのオプション
filepondの強力なのは実は初期状態を表示できる事にある。何のことかって、書いてて何のことだろうってなったので書いてみよう。
まずとりあえず適当にdemoを表示してみる
まずなんか適当に置いたpublic/logo.png を初期状態として表示してみるとする。何ごともまずデモを作って確認してから本番に入る事が必要だ。
の前にeditでもfilepondを出す
ま、これはcreate処理(まあ実際にはIndex)でやったのをそんまま出せばよい。
resources/js/Pages/Posts/Edit.jsx
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, Link, useForm, router } from '@inertiajs/react';
import InputError from '@/Components/InputError';
import InputLabel from '@/Components/InputLabel';
import PrimaryButton from '@/Components/PrimaryButton';
import TextInput from '@/Components/TextInput';
import { formatDistanceToNow } from 'date-fns';
import { ja } from 'date-fns/locale';
import { useState } from 'react';
import { FilePond, registerPlugin } from 'react-filepond';
import 'filepond/dist/filepond.min.css';
import FilePondPluginImagePreview from 'filepond-plugin-image-preview';
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css';
registerPlugin(FilePondPluginImagePreview);
export default function PostEdit({ auth, targetedPost }) {
const { data, setData, post, put, processing, errors, reset } = useForm({
content: targetedPost.content,
});
const [files, setFiles] = useState([]);
const submit = (e) => {
e.preventDefault();
router.visit(route('posts.update', targetedPost.id), {
method: 'put',
data: {
...data,
files: files.map(file => file.file)
},
onSuccess: () => {
reset();
setFiles([]);
},
});
};
return (
<AuthenticatedLayout
user={auth.user}
header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Edit Post</h2>}
>
<Head title="Edit Post" />
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div className="p-6 text-gray-900">
<form onSubmit={submit}>
<div>
<InputLabel htmlFor="content" value="Content" />
<TextInput
id="content"
type="text"
name="content"
value={data.content}
className="mt-1 block w-full"
isFocused={true}
onChange={(e) => setData('content', e.target.value)}
/>
<InputError message={errors.content} className="mt-2" />
</div>
<div className="mt-4">
<FilePond
files={files}
onupdatefiles={setFiles}
allowMultiple={true}
maxFiles={4}
name="files"
labelIdle='Drag & Drop your files or <span class="filepond--label-action">Browse</span>'
/>
</div>
<div className="flex items-center justify-end mt-4">
<PrimaryButton className="ms-4" disabled={processing}>
Update
</PrimaryButton>
</div>
</form>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
);
}
このようにcreateするときとほぼほぼ同じようなフォームは、最終的にcomponent化しての共通化を考えるべきだろう。
putが使えない問題
てかいつのまにrouter.visitで書いてたんや… まあさておいて、この状態でfileをuploadしてrequestをddしてみよう
app/Http/Controllers/PostController.php
public function update(Request $request, Post $post): RedirectResponse
{
dd($request->all());
$post->update($request->all());
return redirect(route('posts.index'))
->with('success', 'Updated');
}
すると
このように空配列になってしまう。これは
ここに書いてあるように
_method で偽装してくれということみたいだというわけで修正する。
const submit = (e) => {
e.preventDefault();
router.visit(route('posts.update', targetedPost.id), {
method: 'post',
data: {
...data,
files: files.map(file => file.file),
_method: 'put',
},
onSuccess: () => {
reset();
setFiles([]);
},
});
};
これで対応可能。
改めてファイル(logo.png)の初期状態を管理する
resources/js/Pages/Posts/Edit.jsx
const [files, setFiles] = useState([
{
source: '12345',
options: {
type: 'local',
file: {
name: 'ろご.png',
size: 111111,
type: 'image/png',
},
},
},
{
source: '12346',
options: {
type: 'local',
file: {
name: 'ろご.png',
size: 222222,
type: 'image/png',
},
},
},
]);
このようにmockを並べることができる。この時の表示は以下の通り
このmockupの情報は自己申告みたいな所があるので、ファイル名やファイルサイズなどは正確に自分で与えないといけない。previewが出てないのは何も設定してないからなのだが、これで送信すると
public function update(Request $request, Post $post): RedirectResponse
{
dd($request->all());
このように実際のファイルではなく何となく配列のダミー構造体が送られてくるので受ける時はこれをスルーする必要とかが出てくる。混ざった場合はこんな感じで、実ファイルはobjectとして渡ってくるから判定は簡単っちゃそう。
filepond-plugin-file-posterを使って初期状態のpreviewを挿入する
ここで画像のpreviewを出す場合はfilepond-plugin-file-posterが必要である。以下はofficialのchatgpt翻訳
ってわけで導入する
% ./vendor/bin/sail npm install filepond-plugin-file-poster
設定
const [files, setFiles] = useState([
{
source: '12345',
options: {
type: 'local',
file: {
name: 'ろご.png',
size: 111111,
type: 'image/png',
},
metadata: {
poster: '/logo.png',
},
},
},
的な。なお2つセットすると
みたいになり非常に微妙なわけだがこれはfilepondというよりはスタイリングのせいなので後で余裕があったら考えてみよう。
実際の編集作業
記事が長くなってきたので次回へ
この記事が気に入ったらサポートをしてみませんか?