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.
Top comments (0)