Raghad-Almasri's avatar

Laravel Eloquent: Eager Loading and Counting Votes for Comments

User has many posts, post has many comments, and there's a pivot table "comment_votes" with the following structure:

$table->id();

$table->foreignId('user_id');

$table->foreignId('comment_id');

$table->enum('type', ['upVote', 'downVote']);

I want to get the count of upvotes and downvotes for every comment within the index() function of my PostController:

public function index() {

$posts = Post::with('comments.user','user.postVotes') ->withCount('upvote','downvote')->latest()->get(); /upvote and downvote are two relationships are used to retrieve the vote count for a post./

}

I'm particularly interested in optimizing the query to minimize database queries and improve performance.

1 like
4 replies
Raghad-Almasri's avatar

class Comment extends Model {

public function user()
{
    return $this->belongsTo(User::class);
}
public function post()
{
    return $this->belongsTo(Post::class);
}
public function votes()
{
    return $this->belongsToMany(User::class,'comment_votes')
        ->withPivot('type')
        ->withTimestamps();
}

}

class Post extends Model {

public function user()
{
    return $this->belongsTo(User::class);
}
public function comments()
{
    return $this->hasMany(Comment::class);
}
public function votes()
{
    return $this->belongsToMany(User::class, 'post_votes')
        ->withPivot('type')
        ->withTimestamps();
}

}

class User extends Authenticatable {

public function posts()
{
    return $this->hasMany(Post::class);
}

public function comments()
{
    return $this->hasMany(Comment::class);
}

public function postVotes()
{
    return $this->belongsToMany(Post::class, 'post_votes')->withPivot('type');
}
public function commentVotes()
{
 return $this->belongsToMany(Comment::class, 'comment_votes')->withPivot('type');
}

}

1 like
Shivamyadav's avatar

@Raghad-Almasri add this to your comment model

public function upvotes()
{
    return $this->votes()->where('type', 'upVote');
}

public function downvotes()
{
    return $this->votes()->where('type', 'downVote');
}

And then in your controller access it like this

public function index()
{
    $posts = Post::with([
        'comments.user', 
        'comments.upvotes',
        'comments.downvotes',
        'user'
    ])
    ->with(['comments' => function($query) {
        $query->withCount(['upvotes', 'downvotes']);
    }])
    ->latest()
    ->get();

    return view('posts.index', compact('posts'));
}
1 like
haisam07's avatar

Isn't something this is what you want ?

$posts = Post::with(['comments' => function ($query) {
    $query->withCount([
        'votes as upvotes_count' => function ($query) {
            $query->where('type', 'upvote');
        },
        'votes as downvotes_count' => function ($query) {
            $query->where('type', 'downvote');
        },
    ]);
}])->get();
1 like

Please or to participate in this conversation.