You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:24:19