Eloquent custom casts let you clean up your Laravel models by automatically transforming data. Learn how to handle complex types without the boilerplate.
Last month, I was debugging a legacy project where every single controller had a manual json_decode call for a specific column. It was messy, error-prone, and frankly, it made me miss the elegance of modern Laravel. If you find yourself repeatedly formatting data every time you pull a record from the database, you're missing out on one of the most powerful features in the framework.
When you’re working with Laravel Eloquent, you often deal with data that doesn't fit neatly into simple strings or integers. Maybe you’ve got a JSON column storing user preferences, or perhaps you're storing encrypted strings that need decryption on the fly.
Instead of cluttering your controllers or services with transformation logic, you can encapsulate that behavior. By using eloquent custom casts, you move the "how" of your data structure into a dedicated class. This keeps your models slim and ensures that your data is always in the expected format the moment it leaves the database.
Laravel comes with built-in casts like integer, boolean, and json, but they don't solve every problem. I once tried to manually map a database status string to a complex Value Object using accessors and mutators. It worked for a while, but as soon as I needed that same logic in three different models, the code became a maintenance nightmare.
When you implement the CastsAttributes interface, you get a clean, reusable way to handle php data transformation. Here is how a basic cast class looks in Laravel 10 or 11:
PHPnamespace App\Casts; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; class JsonObjectCast implements CastsAttributes { public function get($model, string $key, $value, array $attributes) { return json_decode($value, true); } public function set($model, string $key, $value, array $attributes) { return json_encode($value); } }
Once you’ve defined this class, applying it to your laravel models is trivial. You just update the $casts array:
PHPprotected $casts = [ 'settings' => \App\Casts\JsonObjectCast::class, ];
I’ve learned the hard way that just because you can cast something, doesn't mean you should.
Early in my career, I built a cast that performed a database lookup inside the get() method. It turned a simple list view into an N+1 query disaster, adding about 250ms to the request time of a dashboard page. Always keep your cast logic synchronous and lightweight. If your transformation requires an external API call or a heavy database query, keep that logic in a service layer instead.
You should reach for custom casts when:
Money or Address object).If you find yourself writing the same transformation code in multiple places, stop and move it to a cast. Just remember that these classes run on every single access. If you're building a massive data export, keep an eye on your performance metrics.
Yes. You can use the Castable interface or define a class that accepts parameters in the constructor. This is perfect for things like localized currency formatting or dynamic enum mapping.
Casts run after the data is retrieved from the database or just before it is saved. They don't replace your validation logic. You should still use FormRequests to ensure the incoming data is valid before the model tries to cast it.
Absolutely. Since these classes encapsulate specific business logic, you should treat them like any other part of your app. I usually write a quick test to ensure that an array converted to JSON and back again results in the same array structure.
Custom casts are a fundamental tool in the Laravel ecosystem. They help you keep your codebase DRY and ensure that your models remain focused on representation rather than data manipulation.
If I were refactoring that legacy project again, I’d start by identifying every place where a json_decode or serialize call exists. It takes about two days to clean up a large codebase, but the reduction in cognitive load is worth it. What about you? Are you still manually formatting attributes in your controllers, or have you made the switch to custom casts yet?
Mastering Laravel Eloquent model state with exists and wasRecentlyCreated helps you handle database records reliably. Learn to distinguish new from old.