Always use chunkById() instead of chunk() when modifying columns present in your query filters to avoid offset pagination shifts.
Updating records inside standard chunk() shifts database offsets, causing every second chunk of records to be skipped silently. Using chunkById() relies on primary keys (id > last_id) preventing skipped rows.
use App\Models\User;
// ❌ WRONG: Standard chunk() skips records as status changes!
User::where('status', 'pending')->chunk(100, function ($users) {
foreach ($users as $user) {
$user->update(['status' => 'active']);
}
});
// ✅ CORRECT: Keyset chunkById() updates every record safely
User::where('status', 'pending')->chunkById(100, function ($users) {
foreach ($users as $user) {
$user->update(['status' => 'active']);
}
});
- Standard chunk() uses OFFSET pagination which shifts as records leave query criteria
- chunkById() uses keyset WHERE id > last_id pagination for safe updates
- Prevents silent data processing omissions during background migrations
Related Tips
View all tips →Selecting Specific Columns Can Break Eloquent Relationships
Always include foreign key and primary key columns when using select() alongside eager loaded relationships.
Refresh and Pessimistically Lock Models with refreshForUpdate() in Laravel 13.27
Laravel 13.27 adds refreshForUpdate(), reloading an existing model instance in-place with fresh database attributes while acquiring an exclusive FOR UPDATE row lock.