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:
| Cast | Kegunaan |
|---|---|
boolean | Kolom 0/1 menjadi true/false |
integer | Angka string menjadi integer |
float | Angka string menjadi float |
string | Nilai apapun menjadi string |
array | JSON di database menjadi PHP array |
date | String tanggal menjadi Carbon (tanpa waktu) |
datetime | String datetime menjadi Carbon |
timestamp | Datetime menjadi Unix timestamp integer |
hashed | Nilai di-hash saat disimpan (misal password) |
encrypted | Enkripsi/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:
-
Accessor computed — Tambahkan accessor
ringkasan()ke modelCatatanyang mengembalikan 100 karakter pertama dari kolomisiditambahkan tanda...jika teksnya lebih panjang dari itu. Gunakan di view untuk menampilkan preview catatan di halaman listing. -
Cast JSON — Tambahkan kolom
metadatabertipejsonke tabelcatatanvia migration baru. Definisikan castarraydi model. Simpan metadata sederhana saat membuat catatan, lalu baca kembali di controller. -
Format tanggal — Ubah cara
created_atditampilkan di seluruh aplikasi dengan mendefinisikan methodserializeDate()di modelCatatan. 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.