In Laravel, the cursor() method is used to iterate through large database datasets efficiently by keeping only a single record in memory at a time. Unlike get(), which loads the entire result set into an array, cursor() uses PHP generators to yield records one by one.
1. Basic Usage
You can call cursor() at the end of an Eloquent or Query Builder chain. It returns a LazyCollection instance that you can loop through.
use App\Models\User;
foreach (User::where('active', true)->cursor() as $user) {
// Process one user at a time
echo $user->name;
}
2. Key Characteristics
- Single Query Execution: Unlike chunk(), which executes a new query for every batch of records, cursor() executes only one SQL query against the database.
- Memory Efficiency: It significantly reduces memory usage because it doesn't build a massive collection of all retrieved objects in PHP memory.
- Lazy Collections: Starting from Laravel 6.0, cursor() returns a LazyCollection, allowing you to chain collection methods like filter(), map(), and each() without loading everything at once.
- Eager Loading Limitation: Using with() with cursor() can be inefficient because Laravel may still need to load all parent IDs to perform the eager loading query, potentially negating some memory benefits.
Comparison with Other Methods
While cursor() is extremely memory-efficient, Laravel still includes chunk() because they solve different problems. Use chunk() when you need to perform batch operations (like updating records) or load relationships, and use cursor() when memory is your absolute top priority and you only need to read data.
When to use each
The right choice depends on your specific task:
Use chunk() for batch updates or heavy processing.
- Why: It processes data in groups (e.g., 1,000 at a time), which is faster for database operations because it minimizes overhead per record compared to a cursor.
- Safeguard: Use chunkById() if you are updating the same records you are querying to avoid skipping or repeating data.
Use cursor() for massive datasets (1M+ records) or streaming.
- Why: It fetches one record at a time using a single query, keeping memory usage constant regardless of the total result size.
- Best for: Simple exports (like generating a large CSV) or read-only loops where you don't need to load many related models.
Use each() for simple iteration on smaller collections.
- Note: In the context of large datasets, each() is often used as a method on a chunk or collection. If you call Model::all()->each(), you still load everything into memory first, which is dangerous for large sets.

Top comments (0)