Wo33er's avatar

Multi-model Many Relationships

On Laravel 5.1; I've got a group with many users that have many reviews. I want to query all reviews of all users for a given group, paginate and list. So far I've got a users() belongsToMany relationship on the Group model, and a reviews() hasMany relationship on the User model. I feel that there must be a clean means to query and sort such a list leveraging these relationships/models, but all current attempts I've been unable to do so.

My current placeholder implementation, and functions/models mentioned above, are as follows..

User Model;

class User extends BaseModel {
    public function reviews() {
        return $this->hasMany('App\Models\Review', 'user_id')
            ->orderBy('created_at', 'desc');
    }
}

Group Model;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\LengthAwarePaginator as Paginator;
use Illuminate\Pagination;

class Group extends BaseModel {
    public function users() {
        return $this->belongsToMany('App\Models\User', 'users_groups');
    }
        
    public function reviews() {
        $reviews = [];
    
        foreach($this->users()->get() as $user) {
            foreach($user->reviews as $review) {
                array_push($reviews, $review);
            }
        }
        
        return new Paginator($reviews, count($reviews), 25);
    }
}

.. this however doesn't feel efficient, results in broken pagination not rendering as expected and will need me to manually sort the array list by the desired created_at of a users review (group->users->user->completions->created_at).

Group Controller;

public function groupReviews($id) {
    $group = $this->group->findById($id);
    $reviews = $group->reviews();

    return view('groups/group-reviews', compact('group', 'reviews'));
}

Group View;

@forelse($reviews as $review)
    {{ $review->title }} - {{ $review->created_at }}
@empty
    There are no reviews!<br>
@endforelse

Any advice on how to better approach this would be greatly appreciated.

0 likes
4 replies
Wo33er's avatar

Thanks for the reply.

I attempted to use the hasManyThrough method but the example given expects the a unique identifier for the group on the user table, whereas I have users able to be associated to many groups therefore identified through a hasMany on a pivot table users_groups.

Reviews;

  • id
  • user_id
  • created_at

Users;

  • id
  • username

Groups;

  • id
  • name

Users_groups;

  • user_id
  • group_id

Users are associated to the groups per the users() function in the Group class as shown above.

pmall's avatar

@Bloomanity hasManyThrough doesnt work when a belongsToMany is involved.

@Wo33er you can use whereHas to select reviews from a group :

$reviews = Review::whereHas('user.groups', function ($query) use ($group_id) {

    $query->where('id', '=', $group_id);

})->paginate();

it assumes you have set the belongsTo relation Review > User.

Wo33er's avatar

Thanks @pmall that was exactly what I was looking for!

Now have it worked as desired, ordered by review date and paginated in the controller;

public function reviews() {
    $group_id = $this->attributes['id'];

    $reviews = Review::whereHas('user.groups', function ($query) use ($group_id) {
        $query->where('groups.id', '=', $group_id);
    })->orderBy('reviews.created_at', 'desc');

    return $reviews;
}

Please or to participate in this conversation.