Laravel中3+表数据透视表实现:用户多组织多角色场景问询
嘿,这个多组织多角色的权限设计思路非常实用,刚好我在Laravel项目里也经常这么搞!我来帮你把这个方案从数据库到代码落地,一步步讲清楚~
一、数据库结构设计
你提到的三列联合约束表是这个场景的标准解法,我补全完整的SQL定义,并做了一点优化:
CREATE TABLE `organisation_role_user` ( `organisation_id` int(10) unsigned NOT NULL, `role_id` int(10) unsigned NOT NULL, `user_id` int(10) unsigned NOT NULL, PRIMARY KEY (`organisation_id`, `role_id`, `user_id`), -- 用联合主键替代单独的唯一约束,兼顾性能 FOREIGN KEY (`organisation_id`) REFERENCES `organisations`(`id`) ON DELETE CASCADE, FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE, FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
这里用联合主键的原因是:它既可以防止同一个用户在同一个组织下重复拥有同一个角色,还能提升关联查询的性能,比单独加UNIQUE(organisation_id, role_id, user_id)更高效。另外ON DELETE CASCADE配置能保证当组织、角色或用户被删除时,中间表的关联记录会自动清理,避免脏数据。
二、Laravel模型关联配置
接下来咱们把这个多对多关联映射到Laravel的模型里,三个核心模型的写法如下:
User模型
用户和组织、角色的关联需要指定中间表,并带上中间表的额外字段:
namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class User extends Authenticatable { // 用户关联所属组织(附带角色信息) public function organisations(): BelongsToMany { return $this->belongsToMany(Organisation::class, 'organisation_role_user') ->withPivot('role_id') // 取出中间表的role_id字段 ->using(OrganisationRoleUser::class); // 可选:自定义中间表模型时启用 } // 用户关联拥有的角色(按组织分组) public function roles(): BelongsToMany { return $this->belongsToMany(Role::class, 'organisation_role_user') ->withPivot('organisation_id') ->using(OrganisationRoleUser::class); } }
Organisation模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Organisation extends Model { // 组织关联所有用户(附带用户在本组织的角色) public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'organisation_role_user') ->withPivot('role_id') ->using(OrganisationRoleUser::class); } // 组织关联所有被分配的角色 public function roles(): BelongsToMany { return $this->belongsToMany(Role::class, 'organisation_role_user') ->withPivot('user_id') ->using(OrganisationRoleUser::class); } }
Role模型
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; class Role extends Model { // 角色关联所有拥有它的用户 public function users(): BelongsToMany { return $this->belongsToMany(User::class, 'organisation_role_user') ->withPivot('organisation_id') ->using(OrganisationRoleUser::class); } // 角色关联所有分配了它的组织 public function organisations(): BelongsToMany { return $this->belongsToMany(Organisation::class, 'organisation_role_user') ->withPivot('user_id') ->using(OrganisationRoleUser::class); } }
可选:自定义中间表模型
如果需要在中间表上添加额外的业务逻辑(比如快捷权限检查),可以创建一个继承自Pivot的模型:
namespace App\Models; use Illuminate\Database\Eloquent\Relations\Pivot; class OrganisationRoleUser extends Pivot { // 示例:检查该角色是否拥有某个权限 public function hasPermission(string $permission): bool { return $this->role->permissions()->where('name', $permission)->exists(); } }
三、常用业务操作示例
给用户分配组织角色
// 给ID为1的用户,在ID为2的组织里分配ID为3的角色 $user = User::find(1); $user->organisations()->attach(2, ['role_id' => 3]); // 批量分配多个组织角色 $user->organisations()->attach([ 2 => ['role_id' => 3], // 组织2:角色3 4 => ['role_id' => 5] // 组织4:角色5 ]);
验证用户在组织中的角色
// 检查用户是否在组织2中拥有角色3 $hasAdminRole = $user->organisations() ->where('organisations.id', 2) ->wherePivot('role_id', 3) ->exists(); // 获取用户在组织2中的所有角色 $rolesInOrg = $user->roles() ->wherePivot('organisation_id', 2) ->get();
处理用户当前组织
通常用户登录后需要选择当前操作的组织,咱们可以把选中的组织ID存在Session里:
// 存储当前操作的组织ID session(['current_organisation_id' => 2]); // 获取当前组织实例 $currentOrg = Organisation::find(session('current_organisation_id')); // 获取用户在当前组织的角色 $currentRole = $user->roles() ->wherePivot('organisation_id', session('current_organisation_id')) ->first();
四、权限验证拓展
如果需要基于组织角色做细粒度权限控制,可以自定义Laravel Gate或者中间件:
// 在app/Providers/AuthServiceProvider.php的boot方法中定义Gate Gate::define('manage-organisation', function (User $user, Organisation $organisation) { // 验证用户在该组织中是管理员或编辑(假设角色ID 1是管理员,2是编辑) return $user->organisations() ->where('organisations.id', $organisation->id) ->wherePivotIn('role_id', [1, 2]) ->exists(); }); // 在控制器中使用验证 if (Gate::allows('manage-organisation', $organisation)) { // 执行组织管理操作 }
内容的提问来源于stack exchange,提问作者Hitunen
相关产品推荐
相关产品推荐

