如何在Eloquent中配置多模型间的深度关联?
解决多对多嵌套的Eloquent关联问题
我明白你的痛点——Laravel的hasManyThrough确实只适配一对多的层级关联,搞不定咱们这种多层多对多的场景。咱们得通过手动关联查询来实现这些间接关联,下面是每个模型空缺方法的完整实现:
User 模型
class User extends Model { public function userGroups() { return $this->belongsToMany(UserGroup::class, 'user_group_pivot'); } // 获取该用户有权限访问的所有仓库组(含权限信息) public function repoGroups() { return $this->belongsToMany(RepoGroup::class) ->join('user_group_pivot', 'user_groups.id', '=', 'user_group_pivot.user_group_id') ->join('access_rights', 'user_groups.id', '=', 'access_rights.user_group_id') ->where('user_group_pivot.user_id', $this->id) ->select('repo_groups.*', 'access_rights.access') ->groupBy('repo_groups.id'); } // 获取该用户有权限访问的所有仓库(含权限信息) public function repos() { return $this->belongsToMany(Repo::class) ->join('repo_group_pivot', 'repos.id', '=', 'repo_group_pivot.repo_id') ->join('access_rights', 'repo_groups.id', '=', 'access_rights.repo_group_id') ->join('user_group_pivot', 'access_rights.user_group_id', '=', 'user_group_pivot.user_group_id') ->where('user_group_pivot.user_id', $this->id) ->select('repos.*', 'access_rights.access') ->groupBy('repos.id'); } }
UserGroup 模型
class UserGroup extends Model { public function users() { return $this->belongsToMany(User::class, 'user_group_pivot'); } public function repoGroups() { return $this->belongsToMany(RepoGroup::class, 'access_rights')->withPivot(['access']); } // 获取该用户组有权限访问的所有仓库(含权限信息) public function repos() { return $this->belongsToMany(Repo::class) ->join('repo_group_pivot', 'repos.id', '=', 'repo_group_pivot.repo_id') ->join('access_rights', 'repo_groups.id', '=', 'access_rights.repo_group_id') ->where('access_rights.user_group_id', $this->id) ->select('repos.*', 'access_rights.access') ->groupBy('repos.id'); } }
RepoGroup 模型
class RepoGroup extends Model { public function repos() { return $this->belongsToMany(Repo::class, 'repo_group_pivot'); } public function userGroups() { return $this->belongsToMany(UserGroup::class, 'access_rights')->withPivot(['access']); } // 获取有权限访问该仓库组的所有用户(含权限信息) public function users() { return $this->belongsToMany(User::class) ->join('user_group_pivot', 'users.id', '=', 'user_group_pivot.user_id') ->join('access_rights', 'user_groups.id', '=', 'access_rights.user_group_id') ->where('access_rights.repo_group_id', $this->id) ->select('users.*', 'access_rights.access') ->groupBy('users.id'); } }
Repo 模型
class Repo extends Model { public function repoGroups() { return $this->belongsToMany(RepoGroup::class, 'repo_group_pivot'); } // 获取有权限访问该仓库的所有用户组(含权限信息) public function userGroups() { return $this->belongsToMany(UserGroup::class) ->join('repo_group_pivot', 'repo_groups.id', '=', 'repo_group_pivot.repo_group_id') ->join('access_rights', 'repo_groups.id', '=', 'access_rights.repo_group_id') ->where('repo_group_pivot.repo_id', $this->id) ->select('user_groups.*', 'access_rights.access') ->groupBy('user_groups.id'); } // 获取有权限访问该仓库的所有用户(含权限信息) public function users() { return $this->belongsToMany(User::class) ->join('user_group_pivot', 'users.id', '=', 'user_group_pivot.user_id') ->join('access_rights', 'user_groups.id', '=', 'access_rights.user_group_id') ->join('repo_group_pivot', 'repo_groups.id', '=', 'repo_group_pivot.repo_group_id') ->where('repo_group_pivot.repo_id', $this->id) ->select('users.*', 'access_rights.access') ->groupBy('users.id'); } }
关键说明:
- 为什么不用
hasManyThrough?
这个方法仅支持「一对多 → 一对多」的层级关联,而咱们的场景是多层多对多,所以必须通过join手动串联中间表。 groupBy的作用
避免重复数据:比如一个用户可能属于多个用户组,这些组可能同时拥有对同一个仓库/仓库组的权限,groupBy可以确保每个资源只返回一次。- 权限过滤
如果需要只获取write权限的资源,只需在对应的关联方法里添加->where('access_rights.access', 'write')即可。
内容的提问来源于stack exchange,提问作者Johan Fredrik Varen
相关产品推荐
相关产品推荐

