Sayantan Das
Sayantan Das

Reputation: 1641

Laravel relationship query optimization

First of all I apologise for the title, I could not find anything better.

In my project I have Users and Groups. Users can join a group and create a group. The relationships are defined as follows.

User Model

/** Get all the groups the user is administrator of
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function groupsAdmin()
{
    return $this->hasMany('App\Group','group_admin_id','id');
}

Group Model

/** Get the users in a group
 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function users()
{
    return $this->belongsToMany(\App\User::class,'groups_users', 'group_id','user_id');
}

What I am trying to do is get all the users who have joined the groups created by an user. For that I have written a method in my User model:

/**
 * Returns all the users who have attended groups created by this user
 */
public function getPastGroupAttendees()
{
    // first verify if the user is admin of any group
    if(!$this->groupsAdmin)
    {
        return false;
    }

    $attendees = array();

    foreach($this->groupsAdmin as $group)
    {
        if(count($group->users) > 0) $attendees[] = $group->users;
    }
    return $attendees;

}

But the problem with this method is its slow and will get slower with new data. And also as a user can join multiple groups, I would get duplicate users from this method. So if anyone can show me some directions to optimize and correct this it would be very helpful.

Upvotes: 2

Views: 1661

Answers (1)

Alexey Mezenin
Alexey Mezenin

Reputation: 163788

You can setup two relations in User model:

public function groupsAdmin()
{
    return $this->hasMany('App\Group', 'group_admin_id', 'id');
}

public function groups()
{
    return $this->belongsToMany('App\Group');
}

It's one-to-many for admin and many-to-many for groups and users (you'll need pivot table here).

To load the data, use eager loading:

$groupWithUsers = Group::where('group_admin_id', $adminId)->with('users')->first();
$groupsOfUsers = User::with('groups')->get();

To remove duplicates you can iterate over groups and merge() all users collections into one and then use unique() method to remove duplicates.

Another way to do it is to create model for pivot table and get all users of the group with simple and readable code:

$groups = Group::where('group_admin_id', $adminId)->pluck('id'); // Get IDs of groups.
UserGroup::whereIn('group_id', $groups)->get()->unique(); // Get unique users from these groups.

Upvotes: 1

Related Questions