Belajar Livewire - Form Objects (v4)
Episode 10 of 23

Belajar Livewire - Form Objects (v4)

Merapikan form dengan Form Objects v4: class Form khusus untuk menampung state, aturan validasi, dan logika simpan, bind() untuk menghubungkan ke model Eloquent, dan pola create/update terpusat yang bisa dipakai ulang di banyak komponen.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 9 kita membangun form dengan property per field, muncul masalah yang cepat terasa saat aplikasi membesar: form create dan update sering berbagi field yang sama, tapi ditulis duplikat di dua komponen. Mengubah satu aturan validasi berarti mengubah di banyak tempat — dan lupa salah satu tempat akan menghasilkan bug yang membingungkan.

Livewire v4 menjawabnya dengan Form Objects: satu class PHP yang merangkum seluruh state, aturan validasi, dan logika simpan sebuah form. Form Object bisa dipakai ulang di komponen mana pun — dan inilah pola yang disarankan untuk semua form non-trivial di project produksi.

Apa itu Form Object

Form Object adalah class yang extends Livewire\Form, berisi semua yang dibutuhkan satu form:

app/Livewire/Forms/PostForm.php
<?php
 
namespace App\Livewire\Forms;
 
use Livewire\Form;
 
class PostForm extends Form
{
    public string $title = '';
    public string $body = '';
    public string $status = 'draft';
 
    public function rules()
    {
        return [
            'title' => ['required', 'min:3', 'max:255'],
            'body' => ['required', 'min:10'],
            'status' => ['required', 'in:draft,published'],
        ];
    }
}

Perhatikan: ini class PHP murni tanpa concern tentang di mana ia dirender. Ia tahu apa isi form dan aturan apa yang berlaku — tidak tahu halaman apa yang memakainya. Itulah kekuatan reuse-nya.

Menggunakan Form Object di Komponen

Komponen cukup mendeklarasikan property yang ber-tipe Form Object:

app/Livewire/CreatePost.php
<?php
 
namespace App\Livewire;
 
use App\Livewire\Forms\PostForm;
use Livewire\Component;
 
class CreatePost extends Component
{
    public PostForm $form;
 
    public function save()
    {
        $this->form->validate();
 
        \App\Models\Post::create($this->form->all());
 
        session()->flash('status', 'Post dibuat.');
        $this->form->reset();
    }
}

Sekarang property di view dirujuk dengan prefiks form:

resources/views/livewire/create-post.blade.php
<form wire:submit="save">
    <input type="text" wire:model="form.title">
    @error('form.title') <span>{{ $message }}</span> @enderror
 
    <textarea wire:model="form.body"></textarea>
    @error('form.body') <span>{{ $message }}</span> @enderror
 
    <button type="submit">Simpan</button>
</form>

Beberapa hal yang perlu dicatat:

  • wire:model="form.title" — binding menyentuh property di dalam Form Object.
  • $this->form->validate() — menjalankan aturan rules() dari Form Object.
  • Error tampil sebagai form.title, form.body — nama lengkap termasuk prefix.
  • $this->form->reset() — mengosongkan semua field form sekaligus.

bind(): Menghubungkan ke Model

Fitur paling berguna untuk CRUD: bind() memindahkan nilai dari model Eloquent ke Form Object — untuk form update:

app/Livewire/Forms/PostForm.php
use App\Models\Post;
 
public function bind(Post $post)
{
    $this->title = $post->title;
    $this->body = $post->body;
    $this->status = $post->status;
}
app/Livewire/EditPost.php
use App\Livewire\Forms\PostForm;
use Livewire\Component;
use App\Models\Post;
 
class EditPost extends Component
{
    public PostForm $form;
    public Post $post;
 
    public function mount(Post $post)
    {
        $this->post = $post;
        $this->form->bind($post);
    }
 
    public function update()
    {
        $this->form->validate();
 
        $this->post->update($this->form->all());
 
        session()->flash('status', 'Post diperbarui.');
    }
}

Sekarang satu Form Object melayani create (CreatePost) dan update (EditPost). Aturan validasi berubah di satu tempat, kedua halaman ikut mengikuti.

Tip

Pola konsistennya: bind() untuk mengisi dari model (update), all() untuk mengambil nilai ke penyimpanan (create/update), dan reset() untuk mengosongkan. Kalau Form Object kalian mulai menyimpan logika halaman (redirect, flash message, query), itu tanda ia melanggar satu tanggung jawab — pindahkan ke komponen.

Memisahkan Logika Simpan di Form Object

Untuk konsistensi penuh, logika simpan juga bisa tinggal di Form Object:

Form Object dengan metode simpan
use App\Models\Post;
 
public function store()
{
    return Post::create($this->validate());
}
 
public function update(Post $post)
{
    return $post->update($this->validate());
}

Perhatikan validate() mengembalikan data yang sudah divalidasi — array field yang lolos aturan. Dengan begini, kode pemanggil jadi sangat pendek:

Komponen yang ramping
public function save()
{
    $this->form->store();
 
    session()->flash('status', 'Post dibuat.');
    $this->form->reset();
}

Validasi Real-Time pada Form Object

validateOnly juga tersedia di Form Object — pasangan yang rapi untuk feedback per-field:

Validasi real-time per field
public function updatedFormTitle($value)
{
    $this->form->validateOnly('title');
}

Nama hook mengikuti pola updated + nama property lengkap: updatedFormTitle untuk form.title.

Pitfall Umum

  • Prefix form. dihilangkan: wire:model="title" tidak akan menemukan property — harus wire:model="form.title".
  • Form Object yang menjadi terlalu gemuk: jika berisi redirect, flash, atau query halaman, pecah kembali — Form Object harus fokus pada data dan aturannya.
  • bind() di mount() untuk create: untuk form create kosong, cukup default property; bind() hanya untuk update.
  • Lupa use Livewire\Form;: class harus extends Livewire\Form, bukan Component.
  • Error tidak muncul: pastikan @error('form.title') memakai nama lengkap, dan validasi dipanggil ($this->form->validate()).

Penutup

Inti yang harus dibawa pulang:

  • Form Object = class extends Livewire\Form yang merangkum state + aturan validasi.
  • Komponen memakainya lewat public PostForm $form; dan wire:model="form.title".
  • bind() mengisi dari model untuk update; all() untuk data simpan; reset() untuk bersih.
  • Satu Form Object melayani create dan update → konsisten, tanpa duplikasi.
  • validateOnly() tetap tersedia untuk validasi real-time per field.

Di episode 11 selanjutnya kita akan membangun CRUD lengkap dengan pagination & query string — create/read/update/delete dengan Eloquent, pagination dengan trait WithPagination, dan #[Url] agar filter serta pencarian bisa di-share lewat URL. Sampai jumpa di episode 11!

Belajar Livewire - Form Objects (v4) | Belajar Livewire