Prevenir el Lazy Loading o carga peresoza en Laravel 3 formas

- Andrés Cruz

In english

En esta entrada, veremos cómo podemos detectar el Lazy Loading en Laravel; para ello, partiremos del siguiente código:

app\Http\Controllers\Dashboard\PostController.php

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

En donde, un post tiene una relación foránea con las categorías:

class Post extends Model
{
    use HasFactory;

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

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

Desde la vista, referenciamos la categoría, por defecto, Laravel emplea la técnica de lazy loading para obtener los datos relacionados por lo tanto, cada vez que se realice una consulta, se va a realizar una consulta adicional, desde el listado, estamos obteniendo la categoría y con esto una consulta adicional por cada post en la página:

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

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

Lo cual, es el problema del N+1, en donde N en nuestro ejemplo es el tamaño de la página, unos 10 que representan las categorías obtenidas desde el post y el 1 es la consulta principal para obtener los datos paginados.

Por suerte, Laravel en versiones modernas permite detectar este problema muy fácilmente mediante la siguiente configuración:

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());
    }
}

Con el AppServiceProvider podemos cargar clases esenciales de nuestro proyecto para integrarlas en la aplicación.

Así que, si ahora intentamos acceder a la página anterior, veremos un error por pantalla como el siguiente:

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

El sistema de detección del problema N+1 en Laravel no es perfecto, ya que si solamente tuviéramos una paginación de 1 nivel, no ocurriría la excepción anterior.

Otra forma de detectar el problema de N+1, es con un truco adicional, podemos ver las consultas realizadas para resolver una petición del cliente:

routes/web.php

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

Y desde el navegador, veríamos una consulta cada vez que referenciamos la categoría desde el post.

También podemos emplear la extensión de debugbar, si habilitas el script anterior, verás que ocurren más de 15 consultas, una de ellas para la sesión del usuario autenticado, la de los post y 10 para las categorías si tienes una paginación de 10 niveles, Esto es estupendo para detectar el problema pero con el inconveniente de que nuestra página de detalle para la categoría.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.

!Cursos desde!

10$

En Udemy

Quedan 4d 15:02!


Udemy

!Cursos desde!

4$

En Academia

Ver los cursos

!Libros desde!

1$

Ver los libros
¡Hazte afiliado en Gumroad!