Laravel lazy load optimisation - php

I want to load all my comments with one query, for now I have Forum Thread and Replies and comments for both, so I eager load comments seperatly.
$threads = Thread::with('comments');
$replies = Reply::with('comments');
may be something like:
Comment::loadFor([$threads, $replies]);

If you have declared relations in your Comment model for both then You may try this:
$comments = Comment::with('threads', 'replies')->get();
In this case, you need to create relation methods, i.e. threads and replies in your Comment model, for example:
public function replies()
{
return $this->hasMany('Reply', 'comment_id', 'id'); // Could be different
}
public function threads()
{
return $this->hasMany('Thread', 'comment_id', 'id'); // Could be different
}

Related

How to exclude/include specific database columns when eager-loading a nested model in Laravel 8?

To keep things simple, I'll give an example in code to what I'm trying to Achieve.
Let's assume that we have a model called User with the following code:
class User
{
public function posts()
{
return $this->hasOne(Post::class);
}
}
And the Post model look something like this.
class Post
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
And now what I want to be able to do is for example:
$user = Auth::user();
$comments = $user->post->comments; //here, I only want the "comment_id" and "comment_content" fields to be included, not all the Comment Model fields !
you can simply use hasManyThrough in user model to connect comments table.
read about hasManyThrough here: docs
and after having a relation between user and comments (hasManyThrough)
you can use load (Lazy eager loading) method
there is an piece of code you can use here
$user->load(["comments" => function($q){
$q->select("comment_id","comment_content");
}]);
read more about Lazy eager loading: docs

Get reply which having most vote in laravel

I have the reply relationship with like & unlike model. User can like for their best reply and unlike for bad reply.
The like & unlike relationship is almost same,just store in different table.
Now i want to get the replies that having most like & reply that having most unlike, compare them and show only the replies which have most number of vote. How can i achieve it?
In my Discussion Model
public function replies(){
return $this->hasMany('App\Forum\Reply');
}
In Reply Model
public function discussion(){
return $this->belongsTo('App\Forum\Discussion');
}
public function user(){
return $this->belongsTo('App\User');
}
public function likes(){
return $this->hasMany('App\Forum\Like');
}
In the like model
public function user(){
return $this->belongsTo('App\User');
}
public function reply(){
return $this->belongsTo('App\Forum\Reply');
}
I think you can use Eloquents withCount here.
So you'd have something like:
$mostLikedReply = Reply::withCount('likes')->orderBy('likes_count', 'desc')->first();
$mostUnlikedReply = Reply::withCount('unlikes')->orderBy('unlikes_count', 'desc')->first();
Note that withCount will place a {relation}_count column on your resulting models. That's why the orderBy is ordering on what the withCount applies, then by grabbing the first result, it should be the highest liked/unliked reply, from here you can compare what you need.
Read more about counting relations here
How about
$mostLikedReply = $discussion->replies->sortByDesc(function($reply) {
return $reply->likes->count();
}->first();
This will give you the model for the most liked reply.
And the same for most unliked reply and then compare the two? I guess at that point you can maybe merge the whole stuff if you need to.

laravel multilevel relations

Is it possible to make a multi level relation query with Eloquent on a deeper level than 1 level? My tables look like this :
post_comments-> id|comment|post_id|user_id|...
post_comment_replies-> id|reply|post_comment_id|user_id|...
users-> id|name|....
user_data-> id|avatar|...
And so I want to ask is it possible to get the Comments for a Post with all the Replies and the User Data for the person who replied to a comment in 1 query with Eloquent.
This is how my Comments Model looks like:
class PostComment extends Model{
public function replies(){
return $this->hasMany(PostCommentAwnsers::class);
}
public function user() {
return $this->belongsTo(User::class);
}
public function userInfo(){
return $this->belongsTo(UserInfo::class,'user_id','user_id');
}}
public function index($id){
$posts = Post::find($id);
$postComments = PostComment::where('post_id','=',$id)->paginate(5);
return view('post.show',[
'post' => $post,
'postComments' =>$postComments
]);
}
And as I get all the user data for a Comment I want to get all the user data for the person who replied.
I am really sorry if this has been awnsered or documented somewhere else but I just can't seem to find the exact solution to this problem.
You should look for eager loading : https://laravel.com/docs/5.3/eloquent-relationships#eager-loading
if you want to get all posts and their comments :
$posts = Post::with('comment')->get();
and if you want all posts with comments and replies of the comments :
$posts = Post::with('comment.reply')->get();

Laravel simplify nested relation output

I get all items owned by authenticated user.
$items=Auth::user()->with('items')->get();
In my view i can access items collection, but instead of title_id want to retrieve item_title value.
Currently i'm able to get item_title value using code below:
$item->title->title
Is it possible to simplify it to retrieve title like this:
$item->title
?
Here is my models and relations:
Users
id
username
Item
id
user_id
title_id
Item_Titles
id
title
User model:
public function items()
{
return $this->hasMany('Item', 'user_id', 'id');
}
Item model:
public function user(){
return $this->belongsTo('User', 'user_id', 'id');
}
public function title()
{
return $this->belongsTo('ItemTitle','title_id','id');
}
ItemTitle model:
public function item()
{
return $this->hasMany('Item', 'title_id', 'id');
}
UPDATE
Excuse me probably I wasn't clear. To be precise - I just want to find Eloquent alternative to:
$items=Item::where('user_id','=',Auth::id())->leftJoin('item_titles', 'item.title_id', '=', 'item_titles.id')->get();
#foreach ($items as $item)
{{ $item->title }}
#endforeach
Just change your relationship function to
Item model:
public function title()
{
return $this->belongsTo('ItemTitle','title_id','id')->first()->title;
}
You will need to call it as $item->title() unless you also do
public function getTitleAttribute(){
return $this->title();
}
You might get some funny stuff with everything being called 'title' but with this $item->title should also work I think
Yes it is. It looks like you setup a many-to-many relationship with the Item model being the pivot table.
User Model
public function titles()
{
return $this->belongsToMany('ItemTitle', 'items');
}
Note: Change ItemTitle to the correct namespace. Also, change items to the Item model's table name.
You can also define the inverse relationship like this:
ItemTitle Model
public function users()
{
return $this->belongsToMany('User', 'items');
}
From there, you can get all the authenticated user's ItemTitles like this:
$titles = Auth::user()->titles;
Link to the documentation: https://laravel.com/docs/5.1/eloquent-relationships#many-to-many
Editing based on the comments below (thanks to #ash for helping clarify and for his suggestion):
The other answer is more along the lines of what you are trying to achieve so I would recommend taking a look at that. However, there is an error in your question. This does not return items:
$items=Auth::user()->with('items')->get();
That returns all users with their items eager loaded. To see proof of this, if you dd($items), you will most likely see every single user in the database.
That is most likely not what you want to do. To get all items for the authenticated users, you should do this:
$items = Auth::user()->items;
It's that simple to get a collection of items. This will run 2 queries - 1 to get the user and another to get all of his items.

Laravel 5 hasMany relationship on two columns

Is it possible to have a hasMany relationship on two columns?
My table has two columns, user_id and related_user_id.
I want my relation to match either of the columns.
In my model I have
public function userRelations()
{
return $this->hasMany('App\UserRelation');
}
Which runs the query: select * from user_relations where user_relations.user_id in ('17', '18').
The query I need to run is:
select * from user_relations where user_relations.user_id = 17 OR user_relations.related_user_id = 17
EDIT:
I'm using eager loading and I think this will affect how it will have to work.
$cause = Cause::with('donations.user.userRelations')->where('active', '=', 1)->first();
I don't think it's possible to do exactly what you are asking.
I think you should treat them as separate relationships and then create a new method on the model to retrieve a collection of both.
public function userRelations() {
return $this->hasMany('App\UserRelation');
}
public function relatedUserRelations() {
return $this->hasMany('App\UserRelation', 'related_user_id');
}
public function allUserRelations() {
return $this->userRelations->merge($this->relatedUserRelations);
}
This way you still get the benefit of eager loading and relationship caching on the model.
$cause = Cause::with('donations.user.userRelations',
'donations.user.relatedUserRelations')
->where('active', 1)->first();
$userRelations = $cause->donations[0]->user->allUserRelations();
Compoships adds support for multi-columns relationships in Laravel 5's Eloquent.
It allows you to specify relationships using the following syntax:
public function b()
{
return $this->hasMany('B', ['key1', 'key2'], ['key1', 'key2']);
}
where both columns have to match.
I'd prefer doing it this way:
public function userRelations()
{
return UserRelation::where(function($q) {
/**
* #var Builder $q
*/
$q->where('user_id',$this->id)
->orWhere('related_user_id',$this->id);
});
}
public function getUserRelationsAttribute()
{
return $this->userRelations()->get();
}
If anyone landed here like me due to google:
As neither merge() (as suggested above) nor push() (as suggested here) allow eager loading (and other nice relation features), the discussion is still ongoing and was continued in a more recent thread, see here: Laravel Eloquent Inner Join on Self Referencing Table
I proposed a solution there, any further ideas and contributions welcome.
You can handle that things with this smart and easy way .
$cause = Cause::with(['userRelations' => function($q) use($related_user_id) {
$q->where('related_user_id', $related_user_id);
}])->where('active', '=', 1)->first();

Categories