Why loadCount() introduced and its actual usage into laravel - laravel

I already read doc here :
https://github.com/laravel/framework/pull/25997
What i want to know is by using withCount() we were just load count of records instead of getting all relations data.
So by using loadCount() what we can do ?
Please explain in short in simple words.
Thanks

loadCount Eloquent Collection Method introduced by the release of Laravel 5.7.10. According to the laravel-news.
loadCount is the ability to load relationship counts on an Eloquent collection. Before this feature, you could only load relationships, but now you can call loadCount() to get counts for all relations.
The pull request illustrates how you could use loadCount() with the following example:
$events = Event::latest()->with('eventable')->paginate();
$groups = $events->map(function ($event) {
return $event->eventable;
})->groupBy(function ($eventable) {
return get_class($eventable);
});
$groups[Post::class]->loadCount('comments');
$groups[Comment::class]->loadCount('hearts');
return new EventIndexResponse($events);

Related

Laravel: How to retrieve this nested model

In Laravel I have ModelA, ModelB and ModelC. ModelA has many ModelB. ModelB has many ModelC. I want to retrieve all ModelC for a selection of ModelA. How do I do this?
I tried the following:
$models = ModelC::with(['modelB','modelB.modelA' => function ($query) {
$query->where('owner', 123);
}])->get();
But the first query in that case is select * from model_c. Obviously not the result I am looking for.
Imagine that you were received 100 objects from the database, and each record had 1 associated model (i.e. belongsTo). Using an ORM would produce 101 queries by default; one query for the original 100 records, and additional query for each record if you accessed the related data on the model object. In pseudo code, let’s say you wanted to list all published authors that have contributed a post. From a collection of posts (each post having one author) you could get a list of author names like so:
$posts = Post::published()->get(); // one query
$authors = array_map(function($post) {
// Produces a query on the author model
return $post->author->name;
}, $posts);
We are not telling the model that we need all the authors, so an individual query happens each time we get the author’s name from the individual Post model instances.
Eager Loading
As I mentioned, ORMs “lazy” load associations. If you intend to use the associated model data you can trim that 101 query total to 2 queries using eager loading. You just need to tell the model what you need it to load eagerly.
Here’s an example from the Rails Active Record guide on using eager loading. As you can see, the concept is quite similar to Laravel’s eager loading concept.
$posts = Post::with('author')->limit(100)->get();
I find that I receive better understanding by exploring ideas from a wider perspective. The Active Record documentation covers some examples that can further help the idea resonate.
I managed to solve this with nested whereHas calls as follows:
$models = modelC::whereHas('modelB', function ($query) {
$query->whereHas('modelA', function ($query) {
$query->where('owner', 123);
});
})->get();
Laravel to the rescue, yet again!

Laravel eloquent query with sum of related table

I have a table users and posts with columns user_id and post_views.
In post_views I keep information how many times post was display.
And now, in query I would like to get user with sum of post_views all his posts.
I tried do something like this:
User::where(['id'=>$id])->with('posts')->get();
And in model I defined:
public function posts()
{
return $this->hasMany('App\Models\Post')->sum('post_views','AS','totalViews');
}
But without success.
How to do it?
Thank you
You can use a modified withCount():
public function posts()
{
return $this->hasMany('App\Models\Post');
}
$user = User::withCount(['posts as post_views' => function($query) {
$query->select(DB::raw('sum(post_views)'));
}])->find($id);
// $user->post_views
You can use
User::withCount('posts')->find($id)
to get the user with the id $id and a posts_count attribute in the response
I'm not fully sure what the intention of ->sum('game_plays','AS','totalVies'); is - you would need to add more context if you want this
Just something to add with regards to your shown code: No need to query by id using where + the get() at the end will make you query for a collection. If you want to get a single result use find when searching by id
As always laravel has a method for that : withSum (Since Laravel v8)
Note : I know that at the time of the message was posted, the method did not exist, but since I came across this page when I was looking for the same result, I though it might be interesting to share.
https://laravel.com/docs/9.x/eloquent-relationships#other-aggregate-functions
In your case it should be :
$user = User::withSum('posts as total_views', 'post_views')->find($id);
Then you can access to the result :
$user->total_views

Use Laravel 5.3 Query Builder to replicate Eloquent ORM data structure for Sub Query

I am trying to replicate the result set that I get when using Eloquent ORM, except with Laravel Query Builder. Basically using this code I can get the packs to appear nested within the products so that when I loop them on the view I can further loop the packs within each products. Seems pretty basic right (see result set below).
$get_posts_for_product = Product::where('active', 1)
->with('packs')
->get()->toArray();
I have tried a few ways using Query Builder to get this to work but it joins the packs inline as I thought it would.
What is the best way to get this same Array structure using Query Builder, I am aware that the result set is a different type of array and that is fine but for my project it must be done using Query Builder at this point.
Thanks.
I would say, that is why you have Eloquent: you don't have to worry about how to have those relationships together.
However incase you really want to achieve the same result I will demo this using two tables users and messages:
1st method:
Retrieve the users and transform it by querying the database for relationships:
$result = DB::table('users')->get()->transform(function ($user){
$user->messages = DB::table('messages')->where('user_id', $user->id)->get();
return $user;
});
Downside: Having many users means a lot of db query on messages table.
Upside: less codes to write
2nd method:
Retrieve both tables using all the ids of user to query the messages:
$users = DB::table('users')->get();
$messages = DB::table('messages')->whereIn('user_id', $users->pluck('id')->toArray())->get();
$result = $users->transform(function ($user) use ($messages){
$user->messages = $messages->where('user_id', $user->id)->values();
return $user;
});
Downside: The need to still transform it.
Upside: Less database trips. i.e two queries only.
3rd method
Looks like the second except that you can group messages by 'user_id' then you do no extra filter when transforming users result:
$user = DB::table('users')->get();
$messages = DB::table('messages')->whereIn('user_id', $user->pluck('id')->toArray())
->get()
->groupBy('user_id');
$result = $user->transform(function ($user) use ($messages){
$user->messages = $messages[$user->id];
return $user;
});
Downside: Same with two.
Upside: no extra filter when transforming users.
Other method
Join on both users and messages when querying then transform the response, or simply use it as it is.
PS: Eloquent uses query builder.
The answer is open for update.

Laravel 5.1: Eloquent relationship hasmany, Limit records

I have a problem with Laravel 5.1: Eloquent relationship hasmany, Limit records I have 2 tables: feeds, comments. The request is to obtain 5 feeds and comments accordingly to each particular feed. I am currently using the below query:
public function getFeed($user_id){
return Feed::whereUserId($user_id)->with(['comments'])->take(10)->get()->map(function ($feed) {
$feed->comments = $feed->comments->take(5);
return $feed;
});
}
However, it returns all the comments.
My thinking was that the $feed->comments = $feed->comments->take(5); line doesn't work. I only want to get 5 comments for each feed, do you have any advise? Any comments are highly appreciated. Thank you!
It's better late than never, I was facing the same issue yesterday and ended up by sets the entire relations array on the model.
So in your case it will be like this:
return Feed::whereUserId($user_id)->take(10)->get()->map(function($feed) {
$feed->setRelation('comments', $feed->comments->take(5));
return $feed;
});
I was facing the same issue for over a month now. I just wanted to optimize the query because it was taking almost 1.5 sec to load desired data from 1 million records.
I leveraged Eloquent ORM Collection Load() method. This reduces the load time to just 300 ms.
In your case,
return Feed::whereUserId($user_id)
->get()
->each(function ($feed) {
$feed->load('comments')->take(5);
})
});
If it's something that will apply in all cases, you can adjust the relationship in the Feed model like this:
public function comments() {
return $this->hasMany('App\Comment')->limit(5);
}
$feed= Feed::whereUserId($userId)->with(['comments'])->get()->map(function ($query) {
$query->setRelation('comments', $query->comments->take(10));
return $query;
});
Feed::whereUserId($user_id)->with([
'comments' => function($query) {
// You should limit the comments by editing the
// relationships query not the main query.
$query->take(5);
}
])->take(10)->get();
This way you can limit the feed count you want and the comment count each feed has.
the take() eloquent method just adds 'limit' word at the end of the query. this type of query is more complex and isn't supported by vanilla eloquent.
fortunately, there is an additional package called eloquent-eager-limit, which helps with this problem. in order to make it work, install that package by using composer require staudenmeir/eloquent-eager-limit command and put use \Staudenmeir\EloquentEagerLimit\HasEagerLimit; line inside both parent and children model classes.
The error might be here
$feed->comments = $feed->comments->take(5);
Which should be
$feed->comments = $feed->comments->take(5)->get();
I will suggest you write in this way
$feed->load(['comments' => function($query){ return $query->take(5); }])

Laravel Eloquent with and find

Why is this not working?
Article::with('category')->find($ids)
I got a Array to String Conversion Exception.
But if i split the query into 2 parts like this:
$articles = Article::with('category')
and
$articles = $articles->find($ids)
I didn't get any exception and the result is right.
Just for the posterity... other way you can do this is:
Article::with('category')->whereIn('id', $ids)->get();
This should be faster because it's leaving the query to the database manager
Try:
Article::with('category')->get()->find($ids);
You need to get the articles first before you can call find() I believe.
Warning: This retrieves every single article from the database and loads them all into memory, and then selects just one from all that data and returns it. That is probably not how you would want to handle this problem.
This will give you the results based on an array of IDs in Laravel 4
Article::whereIn('id', $ids)->with('category')->get();
Create object and set his properties with relationships
for example, code in your Controller or Service
$article = new Article;
$article = $article->find($id);
$result->article = $article;
$result->article->category = $article->category;
return response()->json($result);
Code in your Model
public function category() {
return $this->hasOne('App\Category');
}

Resources