Accessors and Mutators

An accessor transforms a value on the way out, a mutator on the way in. Both are a protected method named after the attribute in camelCase that returns Attribute::make() with a get closure, a set closure or both; a get for a column that does not exist makes a computed attribute. Customer has mutators that trim and lower-case email and collapse spaces in name, such as Attribute::make(set: fn (string $v) => strtolower(trim($v))), a firstName() accessor returning Attribute::make(get: fn (mixed $v, array $row) => strtok($row['name'], ' ')), and class attributes that shape toArray() and toJson(): #[Hidden(['email'])], #[Appends(['first_name'])].

Mutators on write, accessors and serialization on readSQL
use App\Models\Customer;
$jo = Customer::create(['email' => '  Jo.Park@Example.COM ', 'name' => " Jo   Park\t",
    'country' => 'KR']);
echo $jo->first_name, "\n";
echo Customer::find(3)->toJson(), "\n";
Output
-- insert into `customers` (`email`, `name`, `country`, `created_at`) values
('jo.park@example.com', 'Jo Park', 'KR', '2026-09-23 08:18:57')
Jo
...
{"id":3,"name":"Chen Wei","country":"MY","created_at":"2026-02-02T08:05:00.000000Z",
"first_name":"Chen"}

The mutators cleaned the input before the INSERT, so the unique index on email matches however a customer types it. On the way out, Hidden dropped the email, Appends added first_name, and the date became ISO 8601 in UTC; makeVisible('email') adjusts one instance. For a public API, prefer API resources (Eloquent API Resources), which name every field explicitly.