$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[July 18, 2024] Eloquent ORM

Eloquent Date Casting and Mutators

Eloquent Date Casting: Tips & Tricks

────────────────────────────────────────────────────────

Eloquent Date Casting: Tips & Tricks

Tip: Cast to datetime for Carbon Instances

protected $casts = [
    'published_at' => 'datetime',
    'birthday' => 'date',
];

datetime gives you a full Carbon instance with time. date gives you a Carbon instance at midnight.

Gotcha: $dates is Deprecated

Use $casts instead:

// Old
protected $dates = ['published_at'];

// New
protected $casts = ['published_at' => 'datetime'];

Tip: Carbon Formatting

$post->published_at->format('Y-m-d');
$post->published_at->diffForHumans(); // "2 hours ago"
$post->published_at->toIso8601String();

Gotcha: Timezone Issues

Laravel uses the timezone from config/app.php. If your server is in UTC but your app is in America/New_York, dates can be off.

Tip: Custom Date Format

protected $casts = [
    'created_at' => 'datetime:Y-m-d',
];

Gotcha: now() vs today()

now();   // Current date and time
today(); // Today at 00:00:00

Tip: Carbon Macros

Carbon::macro('isWeekend', function () {
    return $this->dayOfWeek === 0 || $this->dayOfWeek === 6;
});

$post->published_at->isWeekend();

Gotcha: Date Queries

Post::whereDate('created_at', '2024-01-15')->get(); // Just the date
Post::where('created_at', '>=', '2024-01-15 00:00:00')->get(); // Full datetime

Tip: Use cursor() for Memory-Neutral Iteration

When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.

Tip: whereHas() vs load() — Two Different Things

whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.

Gotcha: withCount() Adds a Subquery

withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.

Senior Insight

Date casting in Eloquent seems straightforward until you deal with timezones. I've seen apps where created_at was stored in UTC, displayed in EST, and compared in the application's default timezone — producing bugs that only appeared at certain hours. My rule: always store dates in UTC, always convert to the user's timezone at the presentation layer, and never compare dates in PHP without explicitly specifying the timezone. Carbon's setTimezone() is your friend.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

────────────────────────────────────────────────────────
<-- back to posts