Skip to content

Commit 4342d6d

Browse files
committed
Document the query builder's new lazy() method
1 parent f304a71 commit 4342d6d

File tree

2 files changed

+93
-26
lines changed

2 files changed

+93
-26
lines changed

eloquent.md

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- [Retrieving Models](#retrieving-models)
1212
- [Collections](#collections)
1313
- [Chunking Results](#chunking-results)
14+
- [Streaming Results Lazily](#streaming-results-lazily)
1415
- [Cursors](#cursors)
1516
- [Advanced Subqueries](#advanced-subqueries)
1617
- [Retrieving Single Models / Aggregates](#retrieving-single-models)
@@ -318,9 +319,11 @@ In addition to the methods provided by Laravel's base collection class, the Eloq
318319

319320
Since all of Laravel's collections implement PHP's iterable interfaces, you may loop over collections as if they were an array:
320321

321-
foreach ($flights as $flight) {
322-
echo $flight->name;
323-
}
322+
```php
323+
foreach ($flights as $flight) {
324+
echo $flight->name;
325+
}
326+
```
324327

325328
<a name="chunking-results"></a>
326329
### Chunking Results
@@ -329,47 +332,84 @@ Your application may run out of memory if you attempt to load tens of thousands
329332

330333
The `chunk` method will retrieve a subset of Eloquent models, passing them to a closure for processing. Since only the current chunk of Eloquent models is retrieved at a time, the `chunk` method will provide significantly reduced memory usage when working with a large number of models:
331334

332-
use App\Models\Flight;
335+
```php
336+
use App\Models\Flight;
333337

334-
Flight::chunk(200, function ($flights) {
335-
foreach ($flights as $flight) {
336-
//
337-
}
338-
});
338+
Flight::chunk(200, function ($flights) {
339+
foreach ($flights as $flight) {
340+
//
341+
}
342+
});
343+
```
339344

340345
The first argument passed to the `chunk` method is the number of records you wish to receive per "chunk". The closure passed as the second argument will be invoked for each chunk that is retrieved from the database. A database query will be executed to retrieve each chunk of records passed to the closure.
341346

342347
If you are filtering the results of the `chunk` method based on a column that you will also be updating while iterating over the results, you should use the `chunkById` method. Using the `chunk` method in these scenarios could lead to unexpected and inconsistent results. Internally, the `chunkById` method will always retrieve models with an `id` column greater than the last model in the previous chunk:
343348

344-
Flight::where('departed', true)
345-
->chunkById(200, function ($flights) {
346-
$flights->each->update(['departed' => false]);
347-
}, $column = 'id');
349+
```php
350+
Flight::where('departed', true)
351+
->chunkById(200, function ($flights) {
352+
$flights->each->update(['departed' => false]);
353+
}, $column = 'id');
354+
```
355+
356+
<a name="streaming-results-lazily"></a>
357+
### Streaming Results Lazily
358+
359+
The `lazy()` method works similarly to [the `chunk` method](#chunking-results), executing the query in chunks. However, instead of passing each chunk into a callback, the `lazy()` method returns [a `LazyCollection`](/docs/{{version}}/collections#lazy-collections) of Eloquent models, which lets you interact with the results as a single stream:
360+
361+
```php
362+
use App\Models\Flight;
363+
364+
foreach (Flight::lazy() as $flight) {
365+
//
366+
}
367+
```
368+
369+
Once again, if you are filtering the results based on a column that you will also be updating while iterating over the results, you should use the `lazyById` method. Internally, the `lazyById` method will always retrieve models with an `id` column greater than the last model in the previous chunk:
370+
371+
```php
372+
Flight::where('departed', true)
373+
->lazyById(200, $column = 'id')
374+
->each->update(['departed' => false]);
375+
```
348376

349377
<a name="cursors"></a>
350378
### Cursors
351379

352-
Similar to the `chunk` method, the `cursor` method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records.
380+
Similar to the `lazy` method, the `cursor` method may be used to significantly reduce your application's memory consumption when iterating through tens of thousands of Eloquent model records.
353381

354-
The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor. Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality:
382+
The `cursor` method will only execute a single database query; however, the individual Eloquent models will not be hydrated until they are actually iterated over. Therefore, only one Eloquent model is kept in memory at any given time while iterating over the cursor.
355383

356-
use App\Models\Flight;
384+
> {note} Since the `cursor` method only ever holds a single Eloquent model in memory at a time, it cannot eager load relationships. If you need to eager load models, consider using [the `lazy` method](#streaming-results-lazily) instead.
357385
358-
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
359-
//
360-
}
386+
Internally, the `cursor` method uses PHP [generators](https://www.php.net/manual/en/language.generators.overview.php) to implement this functionality:
387+
388+
```php
389+
use App\Models\Flight;
390+
391+
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
392+
//
393+
}
394+
```
361395

362396
The `cursor` returns an `Illuminate\Support\LazyCollection` instance. [Lazy collections](/docs/{{version}}/collections#lazy-collections) allow you to use many of the collection methods available on typical Laravel collections while only loading a single model into memory at a time:
363397

364-
use App\Models\User;
398+
```php
399+
use App\Models\User;
365400

366-
$users = User::cursor()->filter(function ($user) {
367-
return $user->id > 500;
368-
});
401+
$users = User::cursor()->filter(function ($user) {
402+
return $user->id > 500;
403+
});
369404

370-
foreach ($users as $user) {
371-
echo $user->id;
372-
}
405+
foreach ($users as $user) {
406+
echo $user->id;
407+
}
408+
```
409+
410+
> {note} Although the `cursor` method uses far less memory than a regular query (by only holding a single Eloquent model in memory at a time), it will still eventually run out of memory. This is [due to PHP's PDO driver internally caching all raw query results in its buffer](https://www.php.net/manual/en/mysqlinfo.concepts.buffering.php).
411+
>
412+
> If you're dealing with an enormous amount of data, consider using [the `lazy` method](#streaming-results-lazily) instead.
373413
374414
<a name="advanced-subqueries"></a>
375415
### Advanced Subqueries

queries.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- [Introduction](#introduction)
44
- [Running Database Queries](#running-database-queries)
55
- [Chunking Results](#chunking-results)
6+
- [Streaming Results Lazily](#streaming-results-lazily)
67
- [Aggregates](#aggregates)
78
- [Select Statements](#select-statements)
89
- [Raw Expressions](#raw-expressions)
@@ -154,6 +155,32 @@ If you are updating database records while chunking results, your chunk results
154155

155156
> {note} When updating or deleting records inside the chunk callback, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the chunked results.
156157
158+
<a name="streaming-results-lazily"></a>
159+
### Streaming Results Lazily
160+
161+
The `lazy()` method works similarly to [the `chunk` method](#chunking-results), executing the query in chunks. However, instead of passing each chunk into a callback, the `lazy()` method returns [a `LazyCollection`](/docs/{{version}}/collections#lazy-collections), which lets you interact with the results as a single stream:
162+
163+
```php
164+
use Illuminate\Support\Facades\DB;
165+
166+
DB::table('users')->lazy()->each(function ($user) {
167+
//
168+
});
169+
```
170+
171+
Once again, if you plan to update the retrieved records while iterating over them, it is best to use the `lazyById` method instead. This method will automatically paginate the results based on the record's primary key:
172+
173+
```php
174+
DB::table('users')->where('active', false)
175+
->lazyById()->each(function ($user) {
176+
DB::table('users')
177+
->where('id', $user->id)
178+
->update(['active' => true]);
179+
});
180+
```
181+
182+
> {note} When updating or deleting records while iterating over them, any changes to the primary key or foreign keys could affect the chunk query. This could potentially result in records not being included in the results.
183+
157184
<a name="aggregates"></a>
158185
### Aggregates
159186

0 commit comments

Comments
 (0)