BAB 21: Accessor, Mutator, dan Casts

Memodifikasi nilai atribut model saat diakses, disimpan, atau di-cast menggunakan fitur Eloquent.

Kolom selesai di tabel catatan tersimpan sebagai 0 atau 1 di database. Saat ditampilkan ke pengguna, angka ini perlu diubah menjadi sesuatu yang lebih bermakna. Tanggal created_at perlu diformat berbeda tergantung konteks tampilannya. Kolom preferensi yang menyimpan JSON perlu otomatis menjadi array PHP saat diakses.

Semua transformasi ini bisa ditangani langsung di model menggunakan accessor, mutator, dan casting — tiga fitur Eloquent yang saling melengkapi. Alih-alih mengubah data di controller atau di view setiap kali dipakai, transformasi cukup didefinisikan sekali di model dan berlaku konsisten di seluruh aplikasi.

Accessor: Transformasi Saat Dibaca

Accessor mengubah nilai atribut model saat diakses. Definisikan sebagai method protected yang mengembalikan instance Attribute:

// app/Models/Catatan.php
use Illuminate\Database\Eloquent\Casts\Attribute;

class Catatan extends Model
{
    protected function statusLabel(): Attribute
    {
        return Attribute::make(
            get: fn (mixed $value, array $attributes) =>
                $attributes['selesai'] ? 'Selesai' : 'Belum selesai',
        );
    }
}

Catatan penting di sini: statusLabel bukan kolom di database. Ini adalah computed attribute — atribut yang dihitung dari kolom lain. Closur get menerima nilai kolom itu sendiri sebagai $value, dan array semua atribut model sebagai $attributes di argumen kedua.

Akses attribute ini seperti properti biasa:

$catatan = Catatan::find(1);
echo $catatan->status_label; // "Selesai" atau "Belum selesai"

Perhatikan konvensi nama: method statusLabel() (camelCase) diakses sebagai $catatan->status_label (snake_case). Eloquent menangani konversi ini secara otomatis.

Untuk kolom yang memang ada di database, accessor bisa mengubah nilai sebelum ditampilkan:

protected function judul(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucwords(strtolower($value)),
    );
}

Kini $catatan->judul selalu dikembalikan dengan kapitalisasi yang konsisten, apapun yang tersimpan di database.

Mutator: Transformasi Saat Disimpan

Mutator bekerja kebalikannya: ia mengubah nilai sebelum ditulis ke database. Definisikan dengan closure set di dalam Attribute::make():

protected function judul(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => ucwords(strtolower($value)),
        set: fn (string $value) => trim($value),
    );
}

Sekarang satu method menangani dua arah: saat dibaca, judul diformat dengan kapitalisasi. Saat disimpan, spasi berlebih di awal dan akhir dihapus otomatis.

$catatan = Catatan::find(1);
$catatan->judul = '  belajar laravel  '; // set dipanggil
$catatan->save();

echo $catatan->judul; // "Belajar Laravel" — get dipanggil saat diakses

Mutator bisa juga mengisi beberapa kolom sekaligus. Misalnya, kita ingin menyimpan teks isi dan panjang karakternya secara bersamaan:

protected function isi(): Attribute
{
    return Attribute::make(
        get: fn (string $value) => $value,
        set: fn (string $value) => [
            'isi'           => $value,
            'panjang_teks'  => strlen($value),
        ],
    );
}

Ketika $catatan->isi = 'teks baru' dieksekusi, Eloquent akan menyimpan ke kolom isi dan sekaligus mengisi panjang_teks secara otomatis.

Attribute Casting

Untuk transformasi tipe data yang umum — integer, boolean, JSON ke array, dan lain-lain — kamu tidak perlu menulis accessor/mutator secara manual. Cukup definisikan casting di method casts() model:

// app/Models/Catatan.php
protected function casts(): array
{
    return [
        'selesai'    => 'boolean',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
    ];
}

Dengan casting boolean, kolom selesai yang bernilai 0/1 di database secara otomatis dikembalikan sebagai false/true saat diakses, dan dikonversi kembali saat disimpan. Tidak perlu lagi (bool) $catatan->selesai di mana-mana.

Cast Tipe Umum

Beberapa cast yang paling sering dipakai:

CastKegunaan
booleanKolom 0/1 menjadi true/false
integerAngka string menjadi integer
floatAngka string menjadi float
stringNilai apapun menjadi string
arrayJSON di database menjadi PHP array
dateString tanggal menjadi Carbon (tanpa waktu)
datetimeString datetime menjadi Carbon
timestampDatetime menjadi Unix timestamp integer
hashedNilai di-hash saat disimpan (misal password)
encryptedEnkripsi/dekripsi otomatis via Crypt

Cast untuk JSON/Array

Jika model menyimpan data terstruktur di satu kolom JSON, cast array sangat berguna:

// app/Models/Catatan.php
protected function casts(): array
{
    return [
        'selesai'    => 'boolean',
        'metadata'   => 'array',
        'created_at' => 'datetime',
    ];
}

Anggap kolom metadata menyimpan JSON {"sumber": "mobile", "versi": "2.1"}. Dengan cast array, mengakses dan mengubahnya terasa seperti bekerja dengan array PHP biasa:

$catatan = Catatan::find(1);

// Baca sebagai array
echo $catatan->metadata['sumber']; // "mobile"

// Ubah dan simpan
$meta = $catatan->metadata;
$meta['versi'] = '2.2';
$catatan->metadata = $meta;
$catatan->save();

Saat menggunakan cast array, kamu tidak bisa langsung memodifikasi elemen dalam array menggunakan assignment seperti $catatan->metadata['versi'] = '2.2' — perubahan tidak akan terdeteksi oleh Eloquent karena PHP tidak meneruskan modifikasi array nested melalui magic property. Selalu ambil array ke variabel sementara, ubah, lalu assign kembali.

Cast Tanggal dengan Format Kustom

Cast datetime mengembalikan instance Carbon, library manipulasi tanggal yang sangat kaya fitur. Kamu bisa menentukan format penyimpanan sekaligus:

protected function casts(): array
{
    return [
        'selesai'      => 'boolean',
        'tenggat'      => 'date',
        'selesai_pada' => 'datetime',
        'created_at'   => 'datetime:d M Y',
    ];
}

Dengan 'datetime:d M Y', kolom created_at ketika diakses akan mengembalikan Carbon yang jika dikonversi ke string menghasilkan format “17 Mar 2026”. Di view Blade:

{{ $catatan->created_at->format('d M Y, H:i') }}

Custom Cast

Jika cast bawaan tidak cukup, buat cast class sendiri:

php artisan make:cast AsPrioritas
// app/Casts/AsPrioritas.php
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class AsPrioritas implements CastsAttributes
{
    private const LABEL = [
        'rendah'  => 'Prioritas Rendah',
        'normal'  => 'Prioritas Normal',
        'tinggi'  => 'Prioritas Tinggi',
        'mendesak' => 'MENDESAK',
    ];

    public function get(Model $model, string $key, mixed $value, array $attributes): string
    {
        return self::LABEL[$value] ?? $value;
    }

    public function set(Model $model, string $key, mixed $value, array $attributes): string
    {
        // Terima label tampilan, konversi kembali ke kunci database
        $kunci = array_search($value, self::LABEL);
        return $kunci !== false ? $kunci : strtolower($value);
    }
}

Daftarkan di model:

use App\Casts\AsPrioritas;

protected function casts(): array
{
    return [
        'selesai'   => 'boolean',
        'metadata'  => 'array',
        'prioritas' => AsPrioritas::class,
    ];
}

Sekarang $catatan->prioritas mengembalikan label tampilan, dan saat disimpan, label dikonversi kembali ke kunci yang ringkas.

Latihan

Coba kerjakan latihan berikut untuk mengukuhkan pemahaman:

  1. Accessor computed — Tambahkan accessor ringkasan() ke model Catatan yang mengembalikan 100 karakter pertama dari kolom isi ditambahkan tanda ... jika teksnya lebih panjang dari itu. Gunakan di view untuk menampilkan preview catatan di halaman listing.

  2. Cast JSON — Tambahkan kolom metadata bertipe json ke tabel catatan via migration baru. Definisikan cast array di model. Simpan metadata sederhana saat membuat catatan, lalu baca kembali di controller.

  3. Format tanggal — Ubah cara created_at ditampilkan di seluruh aplikasi dengan mendefinisikan method serializeDate() di model Catatan. Format yang diinginkan: "d F Y" (contoh: “17 Maret 2026”). Verifikasi bahwa format baru muncul konsisten di semua tempat tanpa perlu mengubah kode di controller atau view.

Penutup Bab

Accessor, mutator, dan casting adalah cara Eloquent menjaga model tetap menjadi satu-satunya sumber kebenaran untuk logika transformasi data. Daripada menyebarkan (bool), date('d M Y', strtotime(...)), dan json_decode() di seluruh controller dan view, semua itu cukup ditulis sekali di model.

Sebelum masuk ke studi kasus dan membangun aplikasi CRUD secara lengkap, ada satu langkah persiapan yang sering diabaikan tapi sangat membantu: mengisi database dengan data awal agar pengembangan bisa berjalan tanpa harus input data manual setiap saat. Itulah yang akan kita lakukan di bab berikutnya dengan seeder dan factory.

Referensi

  1. 1Eloquent: Mutators and Casting — Laravel 12.x Documentation
  2. 2Eloquent: Attribute Casting — Laravel 12.x Documentation
  3. 3Carbon — A simple PHP API extension for DateTime