Leveraging Attribute Casting for Advanced Data Transformations in Laravel 12
When working with Eloquent models in Laravel, most developers use attribute casting in a simple way, i.e., booleans, dates, or arrays. But Laravel 12 introduces even more power and flexibility, allowing you to transform model attributes cleanly, elegantly, and securely.
In this in-depth guide, we’ll go beyond the basics to explore:
- Why attribute casting matters
- How custom casts work under the hood
- Real-world casting examples (currency, encryption, geolocation & more)
- How to test and optimize your casts
- Pro tips to avoid common mistakes
Whether you’re a Laravel beginner looking to level up or an advanced developer cleaning up your model logic, this guide will help you build more expressive and maintainable code.
What Is Attribute Casting?
Attribute casting in Laravel allows you to automatically convert database values to a specific type when they are accessed, and back again when they’re stored.
Example of basic casting:
<?php
class User extends Model
{
protected $casts = [
'is_active' => 'boolean',
'email_verified_at' => 'datetime',
'meta' => 'array',
];
}
Now, whenever you access $user->is_active
, you’ll get a boolean true
or false
instead of a 1
or 0
. Similarly, email_verified_at
is automatically converted into a Carbon datetime instance.
This keeps your model logic clean and saves you from writing manual conversions everywhere.
How Casting Works Internally in Laravel 12?
Under the hood, Laravel looks at the $casts
property on your model and determines how to transform data between the database and your application.
- When retrieving data, Laravel runs the get method of the cast (or built-in cast type).
- When saving data, it runs the set method.
- You can use:
- Built-in cast types (
boolean
,datetime
,array
, etc.) - Custom cast classes by implementing the
CastsAttributes
interface - Value object casting (casting directly to a class)
- Built-in cast types (
This means you can completely control how your model values behave, making your code more reusable and testable.
Creating a Simple Custom Cast
Let’s start with something simple.
Say we want to automatically uppercase a name field when retrieved, and lowercase it before storing.
<?php
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class UppercaseCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return strtoupper($value);
}
public function set($model, $key, $value, $attributes)
{
return strtolower($value);
}
}
Register it in your Model:
class User extends Model
{
protected $casts = [
'name' => UppercaseCast::class,
];
}
Now, if you save John Doe
, it'll be stored as john doe
in the database, but retrieved as JOHN DOE
.
Example 1: Casting Currency Values
Let’s say you store prices in cents to avoid floating point precision issues, but want to display them in dollars when accessing them.
<?php
class MoneyCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return number_format($value / 100, 2);
}
public function set($model, $key, $value, $attributes)
{
return (int) ($value * 100);
}
}
Usage in your Model:
<?php
class Product extends Model
{
protected $casts = [
'price' => MoneyCast::class,
];
}
Now:
$product->price = 29.99;
$product->save();
// Database: 2999
// Accessed value: "29.99"
Why this matters: You keep consistent, safe values in your database and avoid formatting logic scattered throughout your views.
Example 2: Encrypted Fields with Automatic Decryption
Sometimes you want to keep sensitive fields encrypted at rest — e.g., tokens, API keys, or personal data.
Laravel makes this simple with a custom cast.
<?php
use Illuminate\Support\Facades\Crypt;
class EncryptedCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
return $value ? Crypt::decryptString($value) : null;
}
public function set($model, $key, $value, $attributes)
{
return $value ? Crypt::encryptString($value) : null;
}
}
Register it:
<?php
class User extends Model
{
protected $casts = [
'ssn' => EncryptedCast::class,
];
}
Whenever you access $user->ssn
, Laravel automatically decrypts it for you. When you save, it’s encrypted automatically.
No more remembering to call Crypt::encryptString()
manually.
Example 3: Geolocation Casting (Latitude/Longitude)
Suppose you store geolocation as a simple string like "40.7128,-74.0060"
.
You can make it more developer-friendly with a GeoCast.
<?php
class GeoCast implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
[$lat, $lng] = explode(',', $value);
return (object) [
'lat' => (float) $lat,
'lng' => (float) $lng,
];
}
public function set($model, $key, $value, $attributes)
{
return "{$value->lat},{$value->lng}";
}
}
Model:
<?php
class User extends Model
{
protected $casts = [
'location' => GeoCast::class,
];
}
Usage:
$user->location = (object) ['lat' => 37.7749, 'lng' => -122.4194];
$user->save();
echo $user->location->lat; // 37.7749
This makes geolocation easy to work with and keeps the database schema simple.
Pro Tip: Casting to Value Objects
Laravel 12 allows casting directly to a class (e.g., a value object) if it implements the right interface.
<?php
class Money
{
public function __construct(public int $amountInCents) {}
public function asDollars(): string
{
return number_format($this->amountInCents / 100, 2);
}
}
Model:
<?php
protected $casts = [
'price' => Money::class,
];
Usage:
echo $product->price->asDollars(); // "29.99"
This aligns perfectly with domain-driven design and keeps business logic separate from models.
Testing Custom Casts
You can test custom cast classes independently of your model.
it('casts money correctly', function () {
$cast = new MoneyCast();
$this->assertEquals('29.99', $cast->get(null, 'price', 2999, []));
$this->assertEquals(2999, $cast->set(null, 'price', 29.99, []));
});
- This ensures your casts behave predictably
- Makes refactoring safer
- Reduces bugs in your application
Performance & Caching Tips
- Use casts wisely: complex transformations should be cached.
- Avoid expensive operations (e.g., external API calls) in casts unless cached.
- Prefer value objects for heavy business logic.
- Laravel 12 optimizes cast hydration, so using them efficiently can improve both clarity and performance.
When Not to Use Casts
Although casts are powerful, they’re not always the right tool:
- If the transformation involves multiple model attributes → consider accessors or services.
- If it requires external data every time → cache or process elsewhere.
- If it’s view-specific formatting (e.g., currency symbols) → handle in Blade or presenters.
Conclusion
Attribute casting in Laravel 12 is more than just a convenient shortcut for dates and booleans. It’s a powerful pattern for clean, reusable, and secure data transformations.
With custom casts, you can:
- Centralize logic like currency, encryption, geolocation
- Keep your models lightweight and expressive
- Boost testability and consistency across your app
- Embrace domain-driven patterns
Tip: Writing reusable casts and sharing them across your models can significantly reduce duplicated code, make your app more robust, and bring your data layer closer to your domain logic.
Process Followed
1. Discover
In this process, I learn more about the requirements from you and/or from the client, and come up with varios permutations and combinations to meet the requirements.
2. Design
Once I learn properly, I do the design of the requirements that you gave keeping things aesthetically pleasing & useable for your audience.
3. Develop
Once you and/or the client is happy with the design(s), I start with the development process of the said requirements.
4. Deploy
After development, I will send the developed task to the client for reviewing. Once confirmed, will be deployed to the live server.
Tech Stack
HTML 5
CSS 3
TailwindCSS
JavaScript
PHP
MySQL
Laravel
VueJs
Spare time Projects
Few of the simple projects simply to learn.