如何动态过滤Laravel Eloquent关联关系的查询结果?
问题
我的Parameter模型包含name和permission两个字段:
- 若
permission字段为空,所有用户均可访问关联模型上的该参数; - 若
permission设为如'see sensitive data'这类值,则仅拥有对应权限的用户可查看该参数(使用Spatie权限包)。
举个例子:Equipment实例关联两个参数,permission为空的'manufacture date',以及permission为'manage equipment'的'serial number',普通用户在Equipment/Show视图中应仅能看到前者。
我尝试在Equipment.php类的Eloquent关联方法中实现该逻辑,但两次尝试均失败:
第一次尝试
public function params(): MorphToMany { return $this->morphToMany(Parameter::class, 'parameterable') ->withPivot('value', 'note')->filter(function ($p) { if (!$p->permission || auth()->user()->can($p->permission)) { return true; } return false; }); }
第二次尝试
public function params(): MorphToMany { $permissions = PermissionService::getAllUserPermissionsArray(); return $this->morphToMany(Parameter::class, 'parameterable') ->withPivot('value', 'note')->whereHas('permission', function ($q) use ($permissions) { $q->whereIn('name', $permissions); }); }
请问该如何正确实现这一需求?
解决方案
为什么之前的尝试失败
- 第一次用的
filter()是集合方法,会先把所有参数从数据库全查出来再过滤,既浪费性能,还会把敏感数据加载到内存里,完全不符合权限控制的安全要求。 - 第二次的
whereHas('permission')逻辑错误:你的Parameter模型里的permission是字段,不是关联关系,而且没处理permission为空的场景。
正确实现方式
要在数据库层面完成过滤,用where条件组合覆盖两种场景:
public function params(): MorphToMany { $query = $this->morphToMany(Parameter::class, 'parameterable') ->withPivot('value', 'note'); // 未登录用户只能查看permission为空的参数 if (!auth()->check()) { return $query->whereNull('permission'); } // 登录用户:要么permission为空,要么permission在用户拥有的权限列表中 $userPermissions = auth()->user()->getAllPermissions()->pluck('name')->toArray(); return $query->where(function ($q) use ($userPermissions) { $q->whereNull('permission') ->orWhereIn('permission', $userPermissions); }); }
额外优化
如果需要在视图里单独判断某个参数是否可见,可以给Parameter模型加个辅助方法:
// Parameter.php public function isVisibleToUser(): bool { if (empty($this->permission)) { return true; } return auth()->check() && auth()->user()->can($this->permission); }
视图中可以这样用:
@foreach($equipment->params as $param) @if($param->isVisibleToUser()) <div>{{ $param->name }}: {{ $param->pivot->value }}</div> @endif @endforeach
不过更推荐用关联查询提前过滤的方式,减少视图层逻辑,也更安全。
内容的提问来源于stack exchange,提问作者narrei
相关产品推荐
相关产品推荐

