如何在Laravel中基于用户信息统一过滤Country模型及嵌套子模型?
基于用户所属国家的模型全局过滤方案
针对你的需求,这里提供几个可行的方案,无需在每个子模型中重复编写whereHas过滤逻辑,将过滤逻辑集中处理:
方案1:给Country模型添加全局作用域
通过全局作用域限制Country模型仅查询当前登录用户所属的国家,子模型通过关联查询时会自动继承该过滤条件。
代码实现
在Country模型中注册全局作用域:
// app/Models/Country.php use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class Country extends Model { protected static function booted() { // 仅对登录用户应用过滤 static::addGlobalScope('user_country', function (Builder $builder) { $user = auth()->user(); if ($user) { $builder->where('id', $user->country_id); } }); } // 关联定义 public function states() { return $this->hasMany(State::class); } }
在LibraryController中查询时,只需通过嵌套关联触发Country的过滤:
// app/Http/Controllers/LibraryController.php public function index() { // 自动返回当前用户所属国家的所有图书馆 $libraries = Library::whereHas('city.state.country')->get(); return $libraries; }
方案2:封装统一的过滤服务类
创建一个服务类集中管理所有模型的国家过滤逻辑,后续维护只需修改这个类即可。
代码实现
// app/Services/CountryFilterService.php use Illuminate\Database\Eloquent\Builder; use App\Models\Country; use App\Models\State; use App\Models\City; use App\Models\Library; class CountryFilterService { public static function apply(Builder $query): Builder { $user = auth()->user(); if (!$user) { return $query; } $model = $query->getModel(); match (true) { $model instanceof Country => $query->where('id', $user->country_id), $model instanceof State => $query->whereHas('country', fn($q) => $q->where('id', $user->country_id)), $model instanceof City => $query->whereHas('state.country', fn($q) => $q->where('id', $user->country_id)), $model instanceof Library => $query->whereHas('city.state.country', fn($q) => $q->where('id', $user->country_id)), default => null }; return $query; } }
控制器中调用方式:
// app/Http/Controllers/LibraryController.php public function index() { $libraries = CountryFilterService::apply(Library::query())->get(); return $libraries; }
方案3:定义全局查询宏
通过全局查询宏给所有Eloquent查询构建器添加统一的过滤方法,调用方式更简洁。
代码实现
在AppServiceProvider中注册宏:
// app/Providers/AppServiceProvider.php use Illuminate\Database\Eloquent\Builder; use App\Models\Country; use App\Models\State; use App\Models\City; use App\Models\Library; public function boot() { Builder::macro('forUserCountry', function () { $user = auth()->user(); if (!$user) { return $this; } $model = $this->getModel(); match (true) { $model instanceof Country => $this->where('id', $user->country_id), $model instanceof State => $this->whereHas('country', fn($q) => $q->where('id', $user->country_id)), $model instanceof City => $this->whereHas('state.country', fn($q) => $q->where('id', $user->country_id)), $model instanceof Library => $this->whereHas('city.state.country', fn($q) => $q->where('id', $user->country_id)), default => null }; return $this; }); }
控制器中直接调用宏方法:
// app/Http/Controllers/LibraryController.php public function index() { $libraries = Library::forUserCountry()->get(); return $libraries; }
方案对比
- 方案1适合仅需限制Country模型查询的场景,子模型依赖关联自动过滤,代码最精简
- 方案2逻辑集中,便于后续扩展更多模型的过滤规则
- 方案3调用方式最简洁,全局通用,推荐作为优先方案
内容的提问来源于stack exchange,提问作者sasani72
相关产品推荐
相关产品推荐

