Laravel, join, where в отношении

Есть такие таблицы

users
| id | email |
roles
| id | title | group_id |
groups
| id | title | server_id |
user_role - Связь пользователя с ролью
| user_id | role_id |

Надо получать роли пользователя из группы определенного сервера

class User extends Authenticatable{

    ...
    
    public function roleGroup(){
        
        $server = request()->get('server');
        return $this->hasOneThrough(Role::class, UserRole::class, 'user_id', 'id', 'id', 'role_id')
                    ->join('groups', 'roles.group_id', 'groups.id')
                    ->where('groups.server_id', $server->id);

    
    }
    
    ...

}

Но это не работает т.к. where не видит join, если это делать через отношение

Если просто добавить метод, то при многократном вызова плодятся запросы к БД

class User extends Authenticatable{

    ...
    
    public function roleGroup(){
        $server = request()->get('server');
        $userRole = UserRole::join('groups', 'user_role.group_id', 'groups.id')
            ->where('user_role.user_id', $this->id)
            ->where('groups.server_id', $server->id)
            ->first();
        
        $user_group_role = Role::find($userRole->role_id);
        return $user_group_role;
    }

    ...
    
}

Подскажите, как такое лучше сделать.


Ответы (2 шт):

Автор решения: IndianCoding

Тут скорее всего можно обойтись без отношения roleGroup в классе User, достаточно использовать whereHas() внутри with()

class User extends Authenticatable {
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

class Role extends Model {
    public function roleGroup()
    {
        return $this->belongsTo(RoleGroup::class, 'group_id');
    }
}

В месте получения запросов:

$users  = User
    ::with(['roles' => function ($query) {
        $query->whereHas('roleGroup', function ($query) {
            $query->where('server_id', request('server'));
        });
    })
    ->get();

foreach ($users as $user) {
    foreach ($user->roles as $role) {
    
    }
}

Логика: для юзеров подгрузить только те роли, у которых группа ролей имеет server_id равный server_id в запросе.

→ Ссылка
Автор решения: Sergey

Решил это так. Выше описал не все нюансы. На сервере, пользователь может состоять только в одной группе У пользователя тогда есть еще отношение $user->group и это отношение учитывает сервер

public function roleGroup(){

    $groupId = $user->group ? $user->group->id : 0;
    return $this->hasOneThrough(Role::class, UserRole::class, 'user_id', 'id', 'id', 'role_id')
        ->where('roles.group_id', $groupId);

}
→ Ссылка