如何为Eloquent模型添加可链式调用的自定义方法?报错原因解析
问题原因分析
你遇到的错误核心原因是方法调用上下文的变化:
当你执行(new Movie())->getMoviesFeaturingTodayOnTheater(...)时,这个方法内部返回的是Illuminate\Database\Eloquent\Builder(查询构建器)实例,而非Movie模型实例。接下来你尝试调用的getBySessionCategory方法只定义在Movie模型类中,查询构建器并没有这个方法,自然会抛出「未定义方法」的错误。
正确解决方案:使用Laravel局部作用域(Local Scopes)
Laravel专门为这种可链式调用的自定义查询逻辑提供了「局部作用域」特性,完美适配你的场景。只需要对模型方法做一点小修改:
步骤1:修改Movie模型的方法定义
把自定义查询方法的前缀改为scope,并且第一个参数接收查询构建器实例:
class Movie extends Model { // 局部作用域方法:前缀必须以scope开头 public function scopeMoviesFeaturingTodayOnTheater($query, $theaterId) { return $query->whereHas( 'sessions.entries.movieTheaterRoom', function ($query) use ($theaterId) { $query->where('movie_theater_id', $theaterId); } ); } public function scopeBySessionCategory($query, $sessionCategory) { // 补充你的whereHas业务逻辑,此处为示例 return $query->whereHas('sessions', function ($query) use ($sessionCategory) { $query->where('category', $sessionCategory); }); } }
步骤2:修改调用方式
调用时去掉方法名中的scope前缀,直接通过查询构建器链式调用:
class MovieResolver { public function getMoviesFeaturingToday(array $args) { $movieQuery = Movie::query() ->moviesFeaturingTodayOnTheater($args['movieTheaterId']) ->bySessionCategory($args['sessioncategory']); return $movieQuery->whereDate('debut', '<=', Carbon::today()) ->orderBy('debut', 'desc') ->get(); } }
为什么这样可行?
局部作用域是Laravel内置的查询扩展机制:
- 当你在查询构建器上调用
moviesFeaturingTodayOnTheater时,Laravel会自动匹配到模型中对应的scopeMoviesFeaturingTodayOnTheater方法 - 方法的第一个参数
$query会自动传入当前的查询构建器实例,你在方法中对$query的修改都会被保留 - 每个局部作用域方法都返回查询构建器实例,所以可以和原生的
whereDate、orderBy等方法无缝链式调用
内容的提问来源于stack exchange,提问作者Eleandro Duzentos
相关产品推荐
相关产品推荐

