Eager loading (Carga ansiosa) con condiciones en Laravel

- Andrés Cruz

In english

Al acceder a las relaciones de Eloquent como propiedades, los datos de la relación se "cargan de forma diferida" o las conocidas como lazy loading o carga perezosa. 

Esto significa que los datos de la relación no se cargan realmente hasta que se accede por primera vez a la propiedad. 

Sin embargo, Eloquent puede "cargar ansiosamente" relaciones en el momento en que consulta el modelo principal. La carga ansiosa alivia el problema de las consultas N + 1. 

Para ilustrar el problema de las consultas N + 1, considere los siguientes modelos:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\URL;

class Tutorial extends Model
{
    use HasFactory;

    protected $fillable = [***];

    public function sections()
    {
        return $this->hasMany(TutorialSection::class)->orderBy('orden');
    }
}
class TutorialSection extends Model
{
    use HasFactory;

    protected $fillable = [***];

    public function tutorial()
    {
        return $this->belongsTo(Tutorial::class);
    }

    public function classes()
    {
        return $this->hasMany(TutorialSectionClass::class)->orderBy('orden');
    }
}
class TutorialSectionClass extends Model
{
    use HasFactory;


    protected $fillable = [***];

    public function tutorialSection()
    {
        return $this->belongsTo(TutorialSection::class);
    }
    public function comments()
    {
        return $this->hasMany(TutorialSectionClassComment::class);
    }
}

La consulta de:

$tutorials = Tutorial->get();
foreach ($tutorials as $key => $t)
  foreach ($t->sections as $key => $s)

Ejectuará una consulta para recuperar todos los tutoriales:

$tutorials = Tutorial->get();

Luego, iteramos todos los tutoriales y a su vez, otbenemos las secciones por cada tutorial; si tenemos 25 tutoriales, sería una consulta adicional a la base de datos por cada tutorial para obtener las secciones; serían 26 consultas, 1 para el tutorial con las secciones y 25 más por cada sección; aquí tenemos el problema conocido como N+1 ya que, segun la cantidad de tutoriales que tengamos, tenemos que hacer la misma cantidad de consultas a la base de datos para obtener sus secciones; pero, la cosa se puede complicar más; si ahora, quetemos obtener las clases:

luego, si queremos obtener las clases:

foreach ($s->classes->get() as $k => $c)

El resultado sería catastrófico; por suerte, en Laravel podemos solventar este problema para que devuelva haga una única consulta a la base de datos.

Eager Loading

En Laravel, el Eager Loading es una técnica de optimización para reducir el número de consultas a la base de datos. Por defecto, cuando se recuperan datos con relaciones en Laravel, se utiliza el Lazy Loading, lo que puede resultar en el problema de la consulta N+1 como se explicó anteriormente.

El Eager Loading es una técnica de carga de datos en la cual, los datos relacionados, se cargan de manera anticipada para evitar la necesidad de cargarlos más tarde cuando se accede a ellos en una única consulta, por lo tanto, con esta técnica se soluciona el problema anterior del N+1; para ello, debemos de usar la función de with indicando el nombre de la relación en el modelo:

$tutorial = Tutorial::with('sections');

Eager Loading Multiple relaciones

Si queremos colocar relaciones adicionales, podemos especificarlas en un array; si las relaciones son anidadas, podemos hacer lo siguiente:

$tutorial = Tutorial::with(['sections','sections.classes' ])

Tal cual puedes ver, colocamos la primera relación:

sections

Y luego la relación más interna:

sections.classes

Eager Loading condiciones

Muchas veces es necesario indicar condiciones adicionales, para ello, podemos especificar una función callback en la cual, colocamos las condiciones internas, en el siguiente ejemplo, queremos que las clases esten posteadas o publicadas:

$tutorial = Tutorial::with('sections')->with(['sections.classes' => function ($query) {
           $query->where('posted', 'yes');
       }])->find($tutorial->id);

Y con esto, puedes crear fácilmente tus relaciones al momento de cargar la relación padre; esto es particularmente útil cuando necesitas construir una Api Rest y necesitas de manera inicial todos los datos, incluyendo la relación padre e hijos.

Definir el eager Loading directamente en los modelos

En los modelos, podemos dejar de manera predefinida el uso del eager loading como vimos anteriormente; para ello, usamos la propiedad de with:

class Tutorial extends Model
{
    ***
    protected $with = ['sections'];
}
Andrés Cruz

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

Andrés Cruz en Udemy