|
1 | | -# laravel-jit-loader |
| 1 | +# liam-wiltshire/laravel-jit-loader |
| 2 | + |
| 3 | +liam-wiltshire/laravel-jit-loader is an extension to the default Laravel Eloquent model to 'very lazy eager load' relationships. |
| 4 | + |
| 5 | +# Very Lazy Eager Load? |
| 6 | +In order to avoid [N+1 issues](https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/), you'd normally load your required relationships while building your collection: |
| 7 | + |
| 8 | +```php |
| 9 | +$books = App\Book::with(['author', 'publisher'])->get(); |
| 10 | +``` |
| 11 | + |
| 12 | +Or otherwise after the fact, but before use: |
| 13 | + |
| 14 | +```php |
| 15 | +$books = App\Book::all(); |
| 16 | + |
| 17 | +if ($someCondition) { |
| 18 | + $books->load('author', 'publisher'); |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +In some situations however, this may not be possible - perhaps front-end developers are able to make changes to templates without touching the code, or perhaps during development you know don't which relationships you'll need anyway. |
| 23 | +This change will track if your models belong to a collection, and if they do and a relationship is called that hasn't already been loaded, the relationship will be loaded across the whole collection just in time for use. |
| 24 | + |
| 25 | +# Does This Work? |
| 26 | +Yes. At least, it does in our production Laravel app. It's also been tested against a (rather constructed) test, pulling out staff, companies and addresses - while this isn't a 'real life' representation, it should give an idea of what it can do: |
| 27 | + |
| 28 | +```php |
| 29 | + public function handle() |
| 30 | + { |
| 31 | + //Count the number of queries |
| 32 | + $querycount = 0; |
| 33 | + DB::listen(function ($query) use (&$querycount) { |
| 34 | + $querycount++; |
| 35 | + }); |
| 36 | + |
| 37 | + $startTime = microtime(true); |
| 38 | + |
| 39 | + |
| 40 | + $staff = Staff::where('name', 'LIKE', 'E%')->orWhere('name', 'LIKE', 'P%')->get(); |
| 41 | + |
| 42 | + /** |
| 43 | + * @var Staff $st |
| 44 | + */ |
| 45 | + foreach ($staff as $st) { |
| 46 | + /** |
| 47 | + * @var Company $company |
| 48 | + */ |
| 49 | + $company = $st->company; |
| 50 | + echo "\n\nName: {$st->name}\n"; |
| 51 | + echo "Company Name: {$company->name}\n"; |
| 52 | + foreach ($company->address as $address) { |
| 53 | + echo "Addresses: {$address->address_1}, "; |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + $endTime = microtime(true); |
| 58 | + |
| 59 | + echo "\n\n=========================\n\n"; |
| 60 | + echo "Queries Run: {$querycount}\n"; |
| 61 | + echo "Execution Time: " . ($endTime - $startTime) . "\n"; |
| 62 | + echo "Memory:" . memory_get_peak_usage(true)/1024/1024 . "MiB"; |
| 63 | + echo "\n\n"; |
| 64 | + } |
| 65 | +``` |
| 66 | + |
| 67 | +Running this locally against a database with 200 companies, 1157 addresses and 39685 staff: |
| 68 | + |
| 69 | +## Without JIT loading: |
| 70 | +Queries Run: 10739 |
| 71 | +Execution Time: 16.058979034424 |
| 72 | +Memory:68MiB |
| 73 | + |
| 74 | + |
| 75 | +## With JIT loading: |
| 76 | +Queries Run: 6 |
| 77 | +Execution Time: 1.6715261936188 |
| 78 | +Memory:26MiB |
| 79 | + |
| 80 | +# Installation |
| 81 | +liam-wiltshire/laravel-jit-loader is available as a composer package: |
| 82 | +`composer require liam-wiltshire/laravel-jit-loader` |
| 83 | + |
| 84 | +Once installed, have your models extend the `\LiamWiltshire\LaravelJitLoader\Model` class instead of the default eloquent model, and JIT loading will be automatically enabled. |
| 85 | + |
| 86 | +# Limitations |
| 87 | +This is an early release based on specific use cases. At the moment the autoloading will only be used when the relationship is loaded like a property e.g. `$user->company->name` instead of `$user->company()->first()->name`. I am working on supporting relations loaded in alternate ways, however there is more complexity in that so there isn't a fixed timescale as of yet! |
| 88 | + |
| 89 | +With any eager loading, a sufficiently large collection can cause memory issues. The JIT model specifies a threshold for autoloading. This is set to 6000 by default, but can be changed by overriding the `$autoloadThreshold` property on a model-by-model basis. |
0 commit comments