Eager loading with conditions in Laravel

- Andrés Cruz

En español
Eager loading with conditions in Laravel

When accessing Eloquent relationships as properties, the data in the relationship is "lazy loaded" or known as lazy loading.

This means that the relationship data is not actually loaded until the property is first accessed.

However, Eloquent can "eager loading" relationships at the time it queries the parent model. Eager loading alleviates the problem of N+1 queries.

To illustrate the N + 1 query problem, consider the following models:

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

The query of:

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

It will execute a query to retrieve all the tutorials:

$tutorials = Tutorial->get();

Then, we iterate all the tutorials and, in turn, we obtain the sections for each tutorial; if we have 25 tutorials, it would be an additional query to the database for each tutorial to get the sections; It would be 26 queries, 1 for the tutorial with the sections and 25 more for each section; here we have the problem known as N+1 since, depending on the number of tutorials we have, we have to make the same number of queries to the database to obtain its sections; but, things can get more complicated; if now, let's get the classes:

then if we want to get the classes:

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

The result would be catastrophic; fortunately, in Laravel we can solve this problem so that it returns a single query to the database.

Eager Loading

In Laravel, Eager Loading is an optimization technique to reduce the number of queries to the database. By default, when retrieving data with relationships in Laravel, Lazy Loading is used, which can result in the N+1 query problem as explained above.

Eager Loading is a data loading technique in which related data is loaded in advance to avoid the need to load it later when it is accessed in a single query, therefore this technique solves the previous N+1 problem; To do this, we must use the with function indicating the name of the relationship in the model:

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

Eager Loading Multiple Relationships

If we want to place additional relations, we can specify them in an array; if the relations are nested, we can do the following:

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

As you can see, we put the first relation:

sections

And then the innermost relationship:

sections.classes

Eager Loading conditions

Many times it is necessary to indicate additional conditions, for this, we can specify a callback function in which we place the internal conditions, in the following example, we want the classes to be posted or published:

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

And with this, you can easily create your relationships at the time of loading the parent relationship; this is particularly useful when you need to build a Rest API and you initially need all the data, including the parent and child relationship.

Define the Eager Loading directly in the models

In the models, we can default to the use of eager loading as we saw previously; to do this, we use the with property:

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

Acepto recibir anuncios de interes sobre este Blog.