$ lexprog.com

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

[June 28, 2024] Eloquent ORM

Eloquent Pruning: Managing Soft Deletes

Eloquent Pruning: Managing Soft Deletes

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

Eloquent Pruning: Managing Soft Deletes

Tip: Schedule Cleanup of Old Soft Deletes

$schedule->call(function () {
    Post::onlyTrashed()
        ->where('deleted_at', '<', now()->subDays(90))
        ->forceDelete();
})->daily();

Gotcha: forceDelete() is Permanent

$post->forceDelete();

No undo. The record is gone forever.

Tip: Restore with Relations

$post->restore();
$post->comments()->withTrashed()->each(fn($c) => $c->restore());

Gotcha: Unique Constraints and Soft Deletes

A soft-deleted record still exists in the database. Use composite unique indexes:

$table->unique(['slug', 'deleted_at']);

Tip: Auto-Delete Relations on Soft Delete

static::deleting(function ($post) {
    if (! $post->isForceDeleting()) {
        $post->comments()->delete();
    }
});

Gotcha: trashed() Check

if ($post->trashed()) {
    // Model is soft deleted
}

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

Soft deletes seem harmless until you forget to scope every query with withoutTrashed() when you actually need deleted records. I've seen admin panels that silently excluded soft-deleted users from reports, skewing metrics for weeks. My advice: use soft deletes only when you have a genuine requirement to recover deleted data (like user accounts). For logs, audit trails, and ephemeral data, hard deletes are simpler and faster.

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

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