Eloquent Cursor: Memory Efficiency
Eloquent Cursor: Memory Efficiency
Eloquent Cursor: Memory Efficiency
Tip: Basic Cursor
foreach (User::cursor() as $user) {
process($user);
}
Yields one model at a time.
Gotcha: No Eager Loading
cursor() doesn't support with(). Load relations manually within the loop.
Tip: lazy() for Chunked Cursor
User::lazy(500)->each(fn($user) => process($user));
Processes 500 at a time, balancing memory and performance.
Gotcha: Cursor and Updates
Updating models during cursor iteration can cause issues. Use chunkById() instead.
Tip: Cursor with Conditions
foreach (User::where('active', true)->cursor() as $user) {
process($user);
}
Gotcha: PDO Cursor
Under the hood, cursor uses PDO's unbuffered queries. Not all databases support this equally well.
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
Laravel's cursor() method uses PHP generators to iterate large datasets with constant memory usage. It's excellent for exports and data migrations, but it has a critical limitation: the underlying PDO connection must stay open for the entire iteration. If a cursor iteration takes 10 minutes and the database's wait_timeout is 300 seconds, the query fails midway. For long-running cursor operations, I increase the connection timeout or use chunked queries instead.
Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)