Prevent Lazy Loading in Laravel 3 ways

- Andrés Cruz

En español

In this post, we will see how we can detect Lazy Loading in Laravel; to do this, we will start from the following code:

app\Http\Controllers\Dashboard\PostController.php

public function index()
{
    $posts = Post::paginate(10);
    return view('dashboard/post/index', compact('posts'));
}

Where, a post has a foreign relationship with the categories:

class Post extends Model
{
    use HasFactory;

    protected $fillable = ['title', 'slug', 'content', 'category_id', 'description', 'posted', 'image'];

    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}

From the view, we reference the category, by default, Laravel uses the lazy loading technique to obtain the related data therefore, every time a query is made, an additional query will be made, from the list, we are obtaining the category and with this an additional query for each post on the page:

resources\views\dashboard\post\index.blade.php

@foreach ($posts as $p)
     ****
     <td>
        {{ $p->category->title }}
***

Which is the N+1 problem, where N in our example is the size of the page, about 10 representing the categories obtained from the post and 1 is the main query to obtain the paginated data.

Luckily, Laravel in modern versions allows you to detect this problem very easily through the following configuration:

app\Providers\AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Database\Eloquent\Model;
***
class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Model::preventLazyLoading(app()->isProduction());
    }
}

With the AppServiceProvider we can load essential classes from our project to integrate them into the application.

So, if we now try to access the previous page, we will see an error on the screen like the following:

Attempted to lazy load [category] on model [App\Models\Post] but lazy loading is disabled.

The N+1 problem detection system in Laravel is not perfect, since if we only had 1 level pagination, the previous exception would not occur.

Another way to detect the N+1 problem is with an additional trick, we can see the queries made to resolve a client request:

routes/web.php

DB::listen(function ($query){
    echo $query->sql;
  //  Log::info($query->sql, ['bindings' => $query->bindings, 'time' => $query->time]);
});

And from the browser, we would see a query every time we reference the category from the post.

We can also use the debugbar extension, if you enable the previous script, you will see that more than 15 queries occur, one of them for the session of the authenticated user, the one for the posts and 10 for the categories if you have a 10-level pagination. This It's great to detect the problem but with the drawback that our detail page for the category.

Andrés Cruz

Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz In Udemy

I agree to receive announcements of interest about this Blog.