見出し画像

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');
    }

すると

何もわたってこないどころか本文すらない

このように空配列になってしまう。これは

ここに書いてあるように

Uploading files via put or patch is not supported in Laravel. Instead, make the request via post, including a _method field set to put or patch. This is called form method spoofing.

訳: LaravelではPUTまたはPATCHを使用してファイルをアップロードすることはサポートされていません。その代わりに、_methodフィールドをPUTまたはPATCHに設定したPOSTリクエストを使用してください。これはフォームメソッドスプーフィングと呼ばれます。

https://inertiajs.com/manual-visits#method


_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翻訳

File Poster プラグインを使用すると、ファイルアイテム内にカスタム画像を表示することができます。

ファイルメタデータオブジェクトに poster プロパティを追加し、その値として画像URLを設定します。File Poster プラグインがそれを読み取り、画像プレビューのプラグインのようにファイルアイテム内に画像をレンダリングします。

https://pqina.nl/filepond/docs/api/plugins/file-poster/

ってわけで導入する

 % ./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というよりはスタイリングのせいなので後で余裕があったら考えてみよう。

実際の編集作業

記事が長くなってきたので次回へ

この記事が気に入ったらサポートをしてみませんか?