[Laravel11+LIVEWIRE #5]フォーム(Forms)

Laravel
LIVEWIRE LOGO

前回[Laravel11+LIVEWIRE #4]アクション(Actions)でアクションの使い方を学んだ。

今回はフォーム(Forms)をやっていく。
参考)https://livewire.laravel.com/docs/forms

フォームとは

フォームはほとんどの Web アプリケーションのバックボーンであるため、Livewire はフォームを構築するための役立つユーティリティを多数提供します。単純な入力要素の処理から、リアルタイム検証やファイルのアップロードなどの複雑な処理に至るまで、Livewire には、作業を容易にし、ユーザーを満足させるためのシンプルで十分に文書化されたツールが用意されています。

フォームの送信(Submitting a form)

今までの記事で散々やってきた。

やっとバリデーションを実装できる。

app/Livewire/Post/Create.php

       :
use Livewire\\Attributes\\Validate;
       :
    #[Validate('required')]
    public $title;

    #[Validate('required')]
    public $message;
       :
    public function store()
    {

        $this->validate();
       :

resources/views/livewire/post/create.blade.php

        <input type="text" id="title" wire:model="title">
        <div>
            @error('title') <span class="error">{{ $message }}</span> @enderror
        </div>

        <textarea id="message" wire:model="message"></textarea>
        <div>
            @error('message') <span class="error">{{ $message }}</span> @enderror
        </div>

メッセージを未入力でSaveボタンを押すと、エラーメッセージが表示される。

今までの記事で散々やってきた。

やっとバリデーションを実装できる。

app/Livewire/Post/Create.php

       :
use Livewire\\Attributes\\Validate;
       :
    #[Validate('required')]
    public $title;

    #[Validate('required')]
    public $message;
       :
    public function store()
    {

        $this->validate();
       :

resources/views/livewire/post/create.blade.php

        <input type="text" id="title" wire:model="title">
        <div>
            @error('title') <span class="error">{{ $message }}</span> @enderror
        </div>

        <textarea id="message" wire:model="message"></textarea>
        <div>
            @error('message') <span class="error">{{ $message }}</span> @enderror
        </div>

メッセージを未入力でSaveボタンを押すと、エラーメッセージが表示される。

フォームオブジェクトの抽出(Extracting a form object)

フォームを別のファイルに定義して書けるらしいが、今のところはどういうメリットが有るのか想像できない。Requestオブジェクトとして受け取る感じでバリデーションさせるのならメリットがあるが、saveメソッド内にvalidate()と書く以上あまりって感じがする。 読み進めるとFormオブジェクト内で保存処理等があるとコンポーネント側がコンパクトになるっていうところではメリットありそうだが、うーん…悩みどころかなぁ。

複雑なバリデーションを実装しないといけないときは、フォームオブジェクト作ってやったほうがいいのだろうな。ということで、とりあえずやってみるか。

その前に、更新処理ができてないから、これを作っていくか。方式としてはきれいではないが、一覧画面で処理する形にする。

% php artisan make:livewire Post\\\\Edit 
 COMPONENT CREATED  🤙

CLASS: app/Livewire/Post/Edit.php
VIEW:  resources/views/livewire/post/edit.blade.php

app/Livewire/Post/Edit.php

<?php

namespace App\\Livewire\\Post;

use Livewire\\Component;

use App\\Models\\Post;

class Edit extends Component
{
    public Post $post;

    public $title;

    public $message;

    public function mount(Post $post)
    {
        $this->post = $post;
        $this->title = $post->title;
        $this->message = $post->message;
    }

    public function update()
    {
        $this->post->title = $this->title;
        $this->post->message = $this->message;
        $this->post->save();

        return redirect()->to('/posts')
             ->with('status', 'Post updated!');
    }

    public function render()
    {
        return view('livewire.post.edit');
    }
}

resources/views/livewire/post/edit.blade.php

<span>
    <form wire:submit="update">
        <input type="text" id="title" wire:model="title">
        <span>
            @error('title') <span class="error">{{ $message }}</span> @enderror
        </span>

        <textarea id="message" wire:model="message"></textarea>
        <span>
            @error('message') <span class="error">{{ $message }}</span> @enderror
        </span>

        <button type="submit">Update</button>
        <span wire:loading>Updating...</span>
    </form>
</span>

resources/views/livewire/post/item.blade.php

<div wire:key="{{ $post->id }}">
    @livewire(Post\\Edit::class, ['post' => $post])
    @livewire(Post\\Delete::class, ['id' => $post->id])
    <hr />
</div>

これを、ブラウザで表示すると、こんな感じで更新処理も可能となる。見た目はあれだが…

登録・更新コンポーネントからフォームデータを移動していく。

% php artisan livewire:form Post\\\\InputForm

   INFO  Form [app/Livewire/Forms/Post/InputForm.php] created successfully.  

まずは、登録から。

app/Livewire/Forms/Post/InputForm.php

<?php

namespace App\\Livewire\\Forms\\Post;

use Livewire\\Attributes\\Validate;
use Livewire\\Form;

use App\\Models\\Post;

class InputForm extends Form
{
    #[Validate('required|min:5')]
    public $title = '';

    #[Validate('required|min:5')]
    public $message = '';

    public function store()
    {

        $this->validate();

        $post = new Post();
        $post->user_id = 1;
        $post->title = $this->title;
        $post->message = $this->message;
        $post->save();

    }

}

app/Livewire/Post/Create.php

<?php

namespace App\\Livewire\\Post;

use App\\Livewire\\Forms\\Post\\InputForm;
use Livewire\\Component;

use App\\Models\\Post;

class Create extends Component
{
    public InputForm $form;

    public function mount()
    {
        $this->form->title = 'Post title...';
        $this->form->message = '';
    }

    public function store()
    {
        $this->form->store();

        return redirect()->to('/posts')
             ->with('status', 'Post created!');
    }

    public function render()
    {
        return view('livewire.post.create');
    }
}

あー結構スッキリしたね!

resources/views/livewire/post/create.blade.php

<div>
    <form wire:submit="store">
        <label for="title">Title:</label>

        <input type="text" id="title" wire:model="form.title">
        <div>
            @error('form.title') <span class="error">{{ $message }}</span> @enderror
        </div>

        <textarea id="message" wire:model="form.message"></textarea>
        <div>
            @error('form.message') <span class="error">{{ $message }}</span> @enderror
        </div>

        <button type="submit">Save</button>
        <span wire:loading>Saving...</span>
    </form>
    {{ session('status') }}
</div>

titleとかが、form.titleに変更となる。

ブラウザで動作させると、登録は問題なく行える。

更新処理を移動していく。

app/Livewire/Forms/Post/InputForm.php

<?php

namespace App\\Livewire\\Forms\\Post;

use Livewire\\Attributes\\Validate;
use Livewire\\Form;

use App\\Models\\Post;

class InputForm extends Form
{
    public ?Post $post;

    #[Validate('required|min:5')]
    public $title = '';

    #[Validate('required|min:5')]
    public $message = '';

    public function setPost(Post $post)
    {
        $this->post = $post;
        $this->title = $post->title;
        $this->message = $post->message;
    }
:
:

app/Livewire/Post/Edit.php

<?php

namespace App\\Livewire\\Post;

use App\\Livewire\\Forms\\Post\\InputForm;
use Livewire\\Component;

use App\\Models\\Post;

class Edit extends Component
{
    public InputForm $form;

    public function mount(Post $post)
    {
        $this->form->setPost($post);
    }

    public function update()
    {
        $this->form->update();

        return redirect()->to('/posts')
             ->with('status', 'Post updated!');
    }

    public function render()
    {
        return view('livewire.post.edit');
    }
}

resources/views/livewire/post/edit.blade.php

<span>
    <form wire:submit="update">
        <input type="text" id="title" wire:model="form.title">
        <span>
            @error('form.title') <span class="error">{{ $message }}</span> @enderror
        </span>

        <textarea id="message" wire:model="form.message"></textarea>
        <span>
            @error('form.message') <span class="error">{{ $message }}</span> @enderror
        </span>

        <button type="submit">Update</button>
        <span wire:loading>Updating...</span>
    </form>
</span>

titleなどをform.titleに変更していく。

これでブラウザで更新処理を行っても問題なく動作する。

IDの改ざんとかどうやってやるんだろうか…htmlを眺めていると改ざんできそうな感じだな。

今度、バリデーションをやるときに詳しくやってみよう!

フォームフィールドのリセット(Resetting form fields)

現段階では登録後にリダイレクトしているので、フォームフィールドはすべて初期化されている。

リダイレクトしなければ、フォームの値は保持されたままである。

フォームでリセットを行うと、フォーム内の値は初期化される。初期化される値はフォームの初期値となる。

app/Livewire/Forms/Post/InputForm.php

    #[Validate('required|min:5')]
    public $title = 'たいとる';
       :
    public function store()
    {

        $this->validate();

       :

        $this->reset();  ← このメソッドでリセットされる。

    }

app/Livewire/Post/Create.php

    public function store()
    {
        $this->form->store();

        //return redirect()->to('/posts')   ← コメントアウトする。
        //     ->with('status', 'Post created!');
    }

ルールオブジェクトの使用(Using Rule objects)

複雑なバリデーションを使うときにルールを使用することはある。titleはユニークであるように修正する。

少し前にこんな書き方をしたが、ルールと以下だと、以下が優先され、ルールは適用されないので注意すること。

    #[Validate('required|min:5')]
    public $title = '';

では、修正していく。

app/Livewire/Forms/Post/InputForm.php

<?php

namespace App\\Livewire\\Forms\\Post;

use Illuminate\\Validation\\Rule;
use Livewire\\Attributes\\Validate;
use Livewire\\Form;

use App\\Models\\Post;

class InputForm extends Form
{
    public ?Post $post;

    public $title = 'たいとる';

    public $message = '';

    private $kbn = '';

    public function setPost(Post $post)
    {
        $this->post = $post;
        $this->title = $post->title;
        $this->message = $post->message;
    }

    public function rules()
    {
        if ($this->kbn == 'store') {
            $titleUnique = Rule::unique('posts');
        } elseif($this->kbn == 'update') {
            $titleUnique = Rule::unique('posts')->ignore($this->post);
        }
        return [
            'title' => [
                'required',
                'min:5',
                $titleUnique,
            ],
            'message' => 'required|min:5',
        ];

    }

    public function store()
    {

        $this->kbn = 'store';
        $this->validate();

        $post = new Post();
        $post->user_id = 1;
        $post->title = $this->title;
        $post->message = $this->message;
        $post->save();

        $this->reset();

    }

    public function update()
    {
        $this->kbn = 'update';
        $this->validate();

        $this->post->title = $this->title;
        $this->post->message = $this->message;
        $this->post->save();

    }

}

これで登録時と更新時のバリデーションが共通化されていい感じで動作する。

ホントはrequestオブジェクトでstoreなのかupdateなのかをrulesメソッド内で判断したかったが、少し階層が深そうなのでやめておいた。

感想

やっとバリデーションに進んだ。Formオブジェクト内でDB更新処理を記載しているが、本来はモデル内でやるべきなんだろうなと思う。

コメント

タイトルとURLをコピーしました