Laravel 5.5模型使用Scope报错:调用未定义方法isPromotionTypeIdScope()
问题排查与解决方案
看起来你遇到的问题核心是本地Scope的定义位置错误,加上一点命名规范的小问题,导致关联查询找不到对应的Scope方法。我来一步步帮你解决:
问题根源分析
你把scopeIsPromotionTypeIdScope这个本地Scope定义在了当前模型(比如ProductPrice)里,但你调用Scope的地方都是针对Promotion模型的查询:
$this->promotion()->isPromotionTypeIdScope(...):promotion()返回的是BelongsToMany关联,对应的是App\Models\Promotion模型的查询构造器Promotion::where('full', 1)->isPromotionTypeIdScope(...):直接调用的是Promotion模型的查询
而Promotion模型里并没有这个Scope方法,所以才会抛出Call to undefined method的错误。之前直接写查询语句能运行,是因为你直接给Promotion的查询构造器加了条件,和当前模型的Scope无关。
解决方案步骤
1. 把Scope移动到Promotion模型中
首先,把你的Scope方法剪切到app/Models/Promotion.php文件里,同时优化一下命名(去掉冗余的Scope后缀,让调用更清晰):
// app/Models/Promotion.php public function scopeValidForType($query, $promotionTypeId = 0) { if ($promotionTypeId !== 0) { return $query->where('promotion_type_id', $promotionTypeId) ->where('validity_from', '<=', now()) // 用Laravel内置的now()更优雅,自动处理时区 ->where('validity_to', '>=', now()) ->where('active', 1) ->orderByDesc('updated_at') // 快捷方法替代orderBy('updated_at', 'DESC') ->limit(1); } return $query; }
2. 修改所有Scope调用的地方
现在Scope属于Promotion模型了,你需要把原来的isPromotionTypeIdScope调用改成新的方法名validForType:
修正single_promotion方法
public function single_promotion($promotion_type_id = 0) { return $this->promotion()->validForType($promotion_type_id)->first() ?? false; }
修正category_promotion方法(顺便简化链式调用)
public function category_promotion($promotion_type_id = 0) { // 不用多次调用first(),直接链式访问关联关系 return $this->product->category->promotion() ->validForType($promotion_type_id) ->first() ?? false; }
修正full_promotion方法
public function full_promotion($promotion_type_id = 0) { return Promotion::where('full', 1) ->validForType($promotion_type_id) ->first() ?? false; }
3. 额外优化建议
- 如果你需要在当前模型(比如
ProductPrice)里复用这个Scope逻辑,也可以在当前模型里定义一个转发方法,但更推荐的是把和Promotion相关的Scope放在Promotion模型中,符合单一职责原则。 - 用
now()代替手动拼接日期字符串,Laravel会自动处理应用的时区设置,避免潜在的时区问题。
这样修改后,你的Scope调用就能正常工作了,其他模型的Scope没问题也是因为那些Scope定义在了对应的模型里,调用的是该模型的查询构造器。
内容的提问来源于stack exchange,提问作者Анжел Миленов
相关产品推荐
相关产品推荐

