如何优化Eloquent中基于Services表的分类-企业关联查询以支持约束?
最优实现方案
问题核心
你之前用Attribute定义的"关联"本质是返回查询构造器,并非Laravel Eloquent的关联关系实例,因此无法使用whereHas这类关联查询方法。需要将模型间的关系用Eloquent原生关联方法定义,才能支持嵌套筛选、排序等操作。
步骤1:修正模型关联定义
根据你的业务逻辑,模型间的关系是:
- 分类(Category)与企业(Company):多对多,通过
services表作为中间表 - 企业(Company)与服务(Service):一对多
- 服务(Service)与评论(Review):一对多
修改CategoryModel.php的关联方法
替换原来的Attribute实现为多对多关联:
// CategoryModel.php use Illuminate\Database\Eloquent\Relations\BelongsToMany; public function companies(): BelongsToMany { // 参数:关联模型、中间表名、当前模型外键、关联模型外键 return $this->belongsToMany(CompanyModel::class, 'services', 'category_id', 'company_id'); }
补充其他模型的关联
// CompanyModel.php use Illuminate\Database\Eloquent\Relations\HasMany; public function services(): HasMany { return $this->hasMany(ServiceModel::class, 'company_id'); }
// ServiceModel.php use Illuminate\Database\Eloquent\Relations\HasMany; public function reviews(): HasMany { return $this->hasMany(ReviewModel::class, 'service_id'); }
步骤2:修复筛选作用域
现在关联已合法,可以直接通过嵌套whereHas实现评论筛选:
// CategoryModel.php public function scopeHasReviews($query) { // 多层关联筛选:分类 -> 企业 -> 服务 -> 评论 $query->whereHas('companies.services.reviews'); }
扩展:支持复杂筛选与排序
如果需要按评论评分筛选或排序,可直接基于关联链扩展:
// CategoryModel.php // 筛选包含评分≥4的评论的分类 public function scopeWithHighRatedReviews($query, $minRating = 4) { $query->whereHas('companies.services.reviews', function ($q) use ($minRating) { $q->where('rating', '>=', $minRating); }) // 预加载平均评分并排序 ->withAvg(['companies.services.reviews' => function ($q) { $q->selectRaw('avg(rating) as avg_rating'); }], 'avg_rating') ->orderBy('avg_rating', 'desc'); }
为什么原方案无效?
Attribute是Eloquent的属性访问器,仅用于自定义模型属性的读取逻辑,返回的是查询构造器而非关联关系实例。而whereHas、with等方法依赖Eloquent关联关系提供的查询绑定能力,必须用belongsToMany/hasMany等官方关联方法定义关系才能生效。
内容的提问来源于stack exchange,提问作者3m1n3nc3
相关产品推荐
相关产品推荐

