Morcos Gad
Posted on May 23, 2022
I knew that this problem N+1 is one of the problems that affect the performance of my project and I found this article that contains the reasons https://laravel-news.com/laravel-n1-query-problems and solution to this problem and I wanted to share it with you because of the importance of this topic
Four cases to clarify the problem and solve it
- Case 1. "Regular" N+1 Query
// app/Models/Book.php
class Book extends Model
{
public function author()
{
return $this->belongsTo(Author::class);
}
}
// Then, in some Controller:
$books = Book::all();
foreach ($books as $book) {
echo $book->author->name;
}
fix
// Instead of
$books = Book::all();
// You should do
$books = Book::with('author')->get();
- Case 2. Two Important Symbols
public function index()
{
$authors = Author::with('books')->get();
return view('authors.index', compact('authors'));
}
// Blade
@foreach($authors as $author)
<tr>
<td>{{ $author->name }}</td>
<td>{{ $author->books()->count() }}</td>
</tr>
@endforeach
So, the method of relation would query the database for each author. But if you load the data, without () symbols, it will successfully use the eager loaded data $author->books
fix
// Controller
$authors = Author::withCount('books')->get();
// Blade
{{ $author->books_count }}
- Case 3. "Hidden" Relationship in Accessor
// Controller
public function index()
{
$authors = Author::all();
return view('authors.index', compact('authors'));
}
// Blade
@foreach($authors as $author)
<tr>
<td>{{ $author->name }}</td>
<td>{{ $author->is_active ? 'Yes' : 'No' }}</td>
</tr>
@endforeach
That "is_active" is defined in the Eloquent model
use Illuminate\Database\Eloquent\Casts\Attribute;
class Author extends Model
{
public function isActive(): Attribute
{
return Attribute::make(
get: fn () => $this->books->count() > 0,
);
}
}
- Case 4. Be Careful with Packages
Laravel has a great ecosystem of packages, but sometimes it's dangerous to use their features "blindly". You can run into unexpected N+1 queries if you're not careful.
- The Built-In Solution Against N+1 Query
since Laravel 8.43, the framework https://laravel-news.com/disable-eloquent-lazy-loading-during-development
In addition to the Laravel Debugbar for inspection, you can add a code to the prevention of this problem
You need to add two lines of code to app/Providers/AppServiceProvider.php
use Illuminate\Database\Eloquent\Model;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Model::preventLazyLoading(! app()->isProduction());
}
}
Now, if you launch any page that contains an N+1 Query problem, you will see an error page
I have explained only the important points, but to go deeper, the article must be increased and benefited from.
Posted on May 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.