Wednesday, 30 June 2021

Laravel Eager Loading (with) && Lazy Loading && Array format

 https://medium.com/@sdkcodes/laravel-eloquent-eager-loading-9596b15e8b5d

https://stackoverflow.com/questions/26005994/laravel-with-method-versus-load-method


Eager loading is a concept in which when retrieving items, you get all the needed items together with all (or most) related items at the same time. This is in contrast to lazy loading where you only get one item at one go and then retrieve related items only when needed.

To put it in a more database-y 😉 perspective; say you have a database containing two tables — questions, and options. All options are related to questions through the question_id column on them, and of course, a question has multiple options. We need to retrieve our questions from database in order to be displayed in the view. There are two ways we can go about this:

Way 1: We get the questions, start to loop through them, and for each question, we go to the database to retrieve the related options. *That is lazy loading* i.e we are delaying the retrieval of the options until we needed them.

Way 2: Straight from the database, we get the questions and fetch the associated options. So that when we are displaying the questions, we do not make separate trips to the database to fetch the options, rather display the options which already exist on the questions. And this, is *Eager loading*

From this point onward in the article, I’ll speak in more Laravel-esque terms but still keep the idea as generic as possible, so, on we go!

Eager loading helps us nicely solve the N+1 query problem. Let us look at a simple illustration using our former questions and options data.

Let’s assume we have an eloquent model Question

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Question extends Model{
public function options(){
return $this->hasMany(Option::class);
}
}

And an Option model:

<?php
namespace App'
use Illuminate\Database\Eloquent\Model;
class Option extends Model{
public function question(){
return $this->belongsTo(Question::class);
}
}

Let’s again assume we have 20 records in our questions table, to get all questions, all we need to do is:

$questions = Question::all();

Then, to get all the options associated with a questions, we’d do something like:

foreach ($questions as $question){
echo $question->option->count() . "<br>";
}

This is what will happen with the above scenario:

The query will run once for the first time to get the 20 questions, then 20 more queries to get the associated options. That makes it 21 queries in total i.e 20(N) + 1

N + 1

That is practically lazy loading.

However, with eager loading, to get the 20 questions and their options, we only need two queries, which can be achieved as follows:

$questions = Question::with('options')->get();foreach ($questions as $question){
echo $question->option->count();
}

By using the with method, we have simultaneously loaded all 20 questions alongside their options. For better understanding, the resulting SQL query looks like this:

select * from questions

select* from options where question_id IN (1, 2, 3, 4, 5 ... 20)

where (1, 2, 3 ... 20) are the ids of the concerned questions.


th accomplish the same end results—eager loading a related model onto the first. In fact, they both run exactly the same two queries. The key difference is that with() eager loads the related model up front, immediately after the initial query (all()first(), or find(x), for example); when using load(), you run the initial query first, and then eager load the relation at some later point.

"Eager" here means that we're associating all the related models for a particular result set using just one query, as opposed to having to run n queries, where n is the number of items in the initial set.


Eager loading using with()

If we eager load using with(), for example:

$users = User::with('comments')->get(); 

...if we have 5 users, the following two queries get run immediately:

select * from `users`
select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)

...and we end up with a collection of models that have the comments attached to the user model, so we can do something like $users->comments->first()->body.


"Lazy" eager loading using load()

Or, we can separate the two queries, first by getting the initial result:

$users = User::all();

which runs:

select * from `users`

And later, if we decide that we need the related comments for all these users, we can eager load them after the fact:

$users = $users->load('comments');

which runs the 2nd query:

select * from `comments` where `comments`.`user_id` in (1, 2, 3, 4, 5)

...and we end up with the same result, just split into two steps. Again, we can call $users->comments->first()->body to get to the related model for any item.





// Returned Array Results :


https://stackoverflow.com/questions/32038019/how-to-index-returned-array-by-key-with-eager-loading-in-laravel-eloquent


For example for the following eager loading :

public function likes()
{
    return $this->hasMany('App\Models\PostLike', 'post_id', 'post_id');
}

The PostController.php implementing the API call to load the posts and their likes looked like:

$posts = Post::with("likes")->where("group_id", "=", $group_id)->get();
[
    {
        "post_id":1,
        "group_id":1,
        "author_id":1,
        "text":"Some text here.",
        "created_at":"2015-08-13 00:15:08",
        "updated_at":"2015-08-13 00:15:08",
        "likes":[
            {"post_id":1,"user_id":1,"updated_at":"2015-08-14 03:05:48"}
        ]
    }



No comments:

Post a Comment