You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:37:35