cursor() vs lazy() in Laravel — Choose the Right One for Large Datasets
🚀 The Common Scenario
At some point, every Laravel app needs to process thousands or millions of records:
- exporting users
- syncing data
- sending notifications
- running background jobs
- fixing legacy data
Loading everything into memory with get() is dangerous.
Laravel gives us two memory-friendly tools:
cursor()lazy()
They look similar — but they are not interchangeable.
🎯 What cursor() Really Does
foreach (User::cursor() as $user) {
// process user
}
✔ How it works:
- Uses one single database query
- Fetches one row at a time
- Uses a PHP generator
- Extremely low memory usage
⚠️ Important limitation:
- Keeps one DB connection open
- No chunking
- Slow for very large datasets
- Can timeout on long-running jobs
🎯 What lazy() Really Does
foreach (User::lazy() as $user) {
// process user
}
✔ How it works:
- Executes multiple chunked queries
- Loads records in small batches
- Releases DB connection between chunks
- Safer for long-running jobs
✔ Default chunk size:
User::lazy(1000);
🧠 Real-World Examples
✔ Example 1: Sending Emails (Best → lazy())
User::where('active', true)
->lazy(500)
->each(fn ($user) => sendEmail($user));
Why lazy()?
- Long-running job
- Safe chunking
- No DB connection lock
✔ Example 2: Streaming Data to CSV (Best → cursor())
$handle = fopen('php://output', 'w');
foreach (Order::cursor() as $order) {
fputcsv($handle, [$order->id, $order->total]);
}
Why cursor()?
- One row at a time
- Minimal memory
- No need to store batches
✔ Example 3: Data Fix Script (Best → lazy())
Product::whereNull('slug')
->lazy()
->each(fn ($product) => $product->update([
'slug' => Str::slug($product->name)
]));
Safer than cursor() because:
- Updates take time
- Avoids DB connection timeout
⚠️ Common Mistake Developers Make
❌ Using cursor() for everything:
foreach (User::cursor() as $user) {
sleep(1); // BAD
}
This keeps the DB connection open for minutes — risky in production.
🧩 Simple Rule to Remember
Use cursor() for fast, read-only, streaming operations.Use lazy() for long-running or write operations.🏁 Final Thought
Both cursor() and lazy() solve memory issues —
but choosing the wrong one creates performance problems.
cursor()→ ultra-low memory, fast readslazy()→ safer, chunked, production-friendly
Understanding this difference puts you ahead of most Laravel developers.