Chapter Model Events menunjukkan betapa mudahnya menempel listener lewat booted() — dan betapa cepatnya method itu penuh bila setiap event punya logika sendiri. Bayangkan satu model dengan hook creating, updated, deleted, dan restored sekaligus: model membengkak, alur sulit dipindai, dan pengujian makin berbelit. Observer menyelesaikan kekacauan itu dengan satu class khusus yang mengumpulkan semua listener milik sebuah model. Nama methodnya otomatis dipetakan ke nama event, jadi Laravel tahu persis kapan masing-masing harus dipanggil — mekanisme yang sama seperti dokumentasi Eloquent jelaskan pada bagian observers.

Membuat Observer #

Artisan menyiapkan kerangka observer lengkap dengan nama model yang dituju:

bash
php artisan make:observer PostObserver --model=Post

Output yang Dihasilkan:

text
   INFO  Observer [app/Observers/PostObserver.php] created successfully.  

Berkas hasilnya berisi method-method yang sudah bernama tepat seperti event-nya, semua masih kosong:

php
namespace App\Observers;

use App\Models\Post;

class PostObserver
{
    /**
     * Handle the Post "created" event.
     */
    public function created(Post $post): void
    {
        //
    }

    /**
     * Handle the Post "updated" event.
     */
    public function updated(Post $post): void
    {
        //
    }

    /**
     * Handle the Post "deleted" event.
     */
    public function deleted(Post $post): void
    {
        //
    }

    /**
     * Handle the Post "restored" event.
     */
    public function restored(Post $post): void
    {
        //
    }

    /**
     * Handle the Post "force deleted" event.
     */
    public function forceDeleted(Post $post): void
    {
        //
    }
}

Daftar method di atas hanyalah titik awal. Laravel memanggil setiap method observer yang namanya sama dengan event — termasuk event -ing seperti creating dan saving dari chapter sebelumnya. Mekanismenya persis listener biasa: saat observer didaftarkan, Laravel memeriksa seluruh event yang diamati model, lalu mengaitkan method bernama sama pada event itu. Method yang tidak ada cukup dilewati, jadi kita hanya menulis yang diperlukan.

Mendaftarkan Observer #

Di Laravel 13 pendaftaran paling ringkas memakai attribute #[ObservedBy] langsung pada class model:

php
use App\Observers\PostObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;

#[ObservedBy(PostObserver::class)]
class Post extends Model
{
    // ...
}

Attribute ini dibaca saat model di-boot, lalu Laravel mendaftarkan observer secara otomatis. Alternatifnya adalah registrasi manual di AppServiceProvider — pendekatan yang dipakai bila tim ingin satu tempat terpusat untuk melihat semua observer aplikasi:

php
use App\Models\Post;
use App\Observers\PostObserver;

public function boot(): void
{
    Post::observe(PostObserver::class);
}

Kedua cara menghasilkan efek yang sama; pilih satu dan konsistenlah memakainya.

Sekarang kita isi observer dengan tugas nyata: memastikan slug selalu bersih sebelum disimpan. Event creating dipilih karena perubahan harus terjadi sebelum INSERT terkirim:

php
namespace App\Observers;

use App\Models\Post;
use Illuminate\Support\Str;

class PostObserver
{
    /**
     * Handle the Post "creating" event.
     */
    public function creating(Post $post): void
    {
        $slug = Str::slug($post->slug);

        if ($post->slug !== $slug) {
            $post->slug = $slug;
        }
    }

    /**
     * Handle the Post "deleted" event.
     */
    public function deleted(Post $post): void
    {
        // tempat wajar membersihkan cache daftar post
    }
}

Kita uji efeknya dengan menyimpan post yang slug-nya kotor — huruf besar dan spasi:

php
$post = Post::create([
    'user_id' => $budi->id,
    'judul' => 'Belajar Observer',
    'slug' => 'Belajar Observer Laravel',
    'isi' => 'Tulisan tentang observer.',
]);

echo 'judul: ', $post->judul, "\n";
echo 'slug masukan: Belajar Observer Laravel', "\n";
echo 'slug di database: ', $post->fresh()->slug, "\n";
echo 'slug di memori (model): ', $post->slug, "\n";

Output yang Dihasilkan:

text
judul: Belajar Observer
slug masukan: Belajar Observer Laravel
slug di database: belajar-observer-laravel
slug di memori (model): belajar-observer-laravel

Observer menyala di antara Post::create() dan eksekusi INSERT: Str::slug() merapikan nilai menjadi belajar-observer-laravel, query menyimpan versi bersih itu, dan model di memori pun membawa versi yang sama. Tanpa observer, kita harus mengingat normalisasi ini di setiap controller — satu tempat yang terlupa berarti data kotor masuk.

creating atau created? #

Aturan memilihnya sama dengan event biasa, hanya kini ditulis sebagai nama method:

MethodMenyalaCocok untuk
creating, updating, savingsebelum operasimengubah atau menambah atribut model
created, updated, savedsesudah operasiaksi yang butuh baris pasti sudah ada
deleted, restored, forceDeletedsesudah operasipembersihan data turunan, cache, file

Satu properti lagi layak dikenal untuk observer berat. Bila observer mengimplementasikan ShouldQueue agar listener berjalan sebagai queued job, mendeklarasikan public $afterCommit = true; membuat job menunggu hingga transaksi database selesai di-commit — mencegah job memproses data yang bisa saja dibatalkan oleh rollback. Tanpa properti ini, job bisa masuk antrian saat transaksi induk masih berjalan; bila transaksi itu di-rollback, job justru mengolah data yang tidak pernah ada di database.

Observer atau booted()? #

Aspekbooted() di modelObserver
Penempatan logikamenumpuk di dalam class modelclass terpisah di app/Observers
Cocok untuksatu-dua hook pendekbanyak event, logika besar
Keterbacaanmodel cepat membengkakmodel tetap ramping
Pengujianmenyulitkan isolasimudah diuji atau di-mock terpisah

Pembagian kerjanya sederhana: aturan kecil yang tak akan tumbuh (mengisi slug default) sah menetap di booted(), sedangkan logika pendamping yang nyata — normalisasi, notifikasi, pembersihan cache — pantas mendapat rumah sendiri berupa observer. Pola observer juga mempermudah code review karena reviewer cukup membuka satu berkas untuk melihat seluruh reaksi model terhadap siklus hidupnya.

Model kini bisa merespons setiap perubahan secara rapi. Namun ada satu hal yang belum pernah kita lihat secara jujur: query SQL apa sebenarnya yang semua aktivitas Eloquent ini hasilkan? Chapter penutup section ini mengajak kita mengukurnya.