ThinkPHP 5 Model 根本没有 newQuery()——Laravel 老手必踩的 ORM 迁移坑
接手一个用 ThinkPHP 5 + FastAdmin 搭建的后台管理系统,在商品模块新增了一个 Service 层方法,沿用 Laravel 的习惯写下 (new Product())->newQuery()->where(...),随手按下保存,接口立刻 500。
日志里是一行不留情面的 Fatal error,整个请求栈终止在刚写完的那行代码。
现象
Fatal error: Call to undefined method app\common\model\Product::newQuery()
或者 FastAdmin 包装后更精简:
Method not exist: think\model\...\newQuery
两种触发写法:
// 写法 1 —— 实例调用(Laravel 习惯)
$rows = (new Product())->newQuery()
->where('status', 1)
->select();
// 写法 2 —— 静态调用
$rows = Product::newQuery()->where('status', 1)->select();两种都是 500,因为方法根本不存在。
根因
ThinkPHP 5 和 Laravel Eloquent 在 ORM 设计上有一个根本性差异:
Laravel 的架构:Model 是数据容器,Builder 是独立对象。newQuery() 的职责是为当前 Model 创建一个新的 Eloquent\Builder 实例,持有 Model 引用,作为查询入口。
ThinkPHP 5 的架构:Model 类本身通过魔术方法 __call / __callStatic 直接代理到底层 think\db\Query。调用 Product::where(...) 等价于 Laravel 的 Product::query()->where(...),根本不需要显式"创建 Builder"这一步。
翻 TP5 源码 thinkphp/library/think/Model.php,__callStatic 的实现大致是:
public static function __callStatic($method, $args)
{
return call_user_func_array([new static()->db(), $method], $args);
}所以 Product::where(...) 最终等价于 (new Product())->db()->where(...),中间那一层 Builder 工厂在 TP5 里是隐式的。
newQuery() 是 Eloquent 的 API,专门用来获取 Builder 实例。在 ThinkPHP 5 中调用它只会得到 Method not exist 的 Fatal error。TP5 Model 没有 Builder 工厂方法——它自己就是查询代理,无需也无法 newQuery()。
正解
方式 1:静态调用(日常首选)
$rows = Product::where('status', 1)
->where('category_id', $catId)
->order('created_at', 'desc')
->select();直接在 Model 类名上调用查询方法,TP5 魔术代理帮你转发。
方式 2:实例调用
$model = new Product();
$rows = $model->where('status', 1)->select();与静态调用等价,偶尔在需要先设置属性再查询时使用。
方式 3:显式拿 Query 对象
$q = (new Product())->db(); // 返回 think\db\Query
$rows = $q->where('status', 1)->select();db() 才是 TP5 中 newQuery() 的真正等价物,但几乎不需要显式调用——魔术方法已经帮你处理了。
方式 4:绕过 Model 用 Db facade
当不需要模型特性(软删自动过滤、关联、accessor)时,直接用 Db facade 更轻:
use think\Db;
$rows = Db::name('product')->where('status', 1)->select();把 (new Product())->newQuery()->where(...) 改成 Product::where(...) 即可。TP5 的魔术代理让 Model 类名本身就是查询入口,不需要也不存在 newQuery() 这一层。
快速排查存量代码
接手含 Laravel 背景成员写过的 TP5 项目时,跑一下:
grep -rn "->newQuery\|::newQuery" application/ --include="*.php"所有命中都是 Laravel 习惯残留,直接删掉 ->newQuery() / ::newQuery() 改静态调用即可。
一句话外卖
ThinkPHP 5 Model 本身就是 Query 代理,
Model::where()等价于 Laravel 的Model::query()->where()——从 Eloquent 迁过来时,删掉newQuery(),不需要"先拿 Builder 再查询"这个思维定式。