0

I have a model that is "static". Its contains games with various information about them. And users can comment on games with a polymorphic relationship. The problem is that I want to fetch the 10 latest games that has been commented on, and that user for that comment. So Game doesn't have a relationship to Users.

I have manage to solve it like this:

$games = Game::join('comments', 'games.id', '=', 'comments.commentable_id')
->where('comments.commentable_type', Game::class)
->latest('comments.created_at')
->groupBy('games.id')
->take(10)
->withCount('comments')
->get()->each(function($game){
    $game->comment = $game->comments()->orderBy('created_at', 'desc')->first();
    $game->user = User::find($game->comment->user_id);
    return $game;
});

But this creates a lot of queries, I would like to eager load, don't have that N+1 problem everyone is talking about.

I got another solution to add this function to Game

class Game extends Model {
    public function latestComment() {
        return $this->morphOne(Comment::class, 'commentable')->latest();
    }
}

And run the query like $games = Game::withCount('comments')->with(['latestComment.user'])->take(10)->get();

But it seems as this query doesn't fetch the users. Only the first user is fetched. And also the comments_count (withCount()) only returns a value for the firsts result.

So I'm stuck!

The expected result Load the 10 latest games with the latest comment for that game, with the totalt count of comments for that game, with the user for the latest comment - everything eager loaded to avoid the n+1 problem.

Is it possible? ps. prepared a sqlfiddle http://www.sqlfiddle.com/#!9/c07570

Edit It seems as the second query in this question "takes" (take(10)) takes the first 10 games in from games table, regardless if they have comments or not. If I have 15 comments for games with IDs from 20-30, the query will check IDs from 1 to 10.

If I run the second query in my question, with just the fiddle data in my comment table. Result 10 will contain game with id of 10, no user, no comments_count etc.

Jonas Staudenmeir
  • 24,815
  • 6
  • 63
  • 109
Adam
  • 1,231
  • 1
  • 13
  • 37

2 Answers2

0

You can use a modified withCount() to get the latest comments.created_at:

$games = Game::withCount(['comments',
        'comments as latest_comment' => function($query) {
            $query->select(DB::raw('max(created_at)'));
    }])
    ->having('comments_count', '>', 0)
    ->orderByDesc('latest_comment')
    ->with('latestComment.user')
    ->take(10)
    ->get();
Jonas Staudenmeir
  • 24,815
  • 6
  • 63
  • 109
0

I would access directly the comments table/model, query for the last 10 comments, pluck the games ids, and use them to query the games model/table with a whereIn.

Also add to the games query a selectRaw('(SELECT COUNT(*) FROM comments where ...) as commentCount')

Erubiel
  • 2,934
  • 14
  • 32