field()必须在select()前调用且对关联表无效;主模型需显式声明字段,关联表裁剪须在with闭包或关联定义中单独调用field()。

field() 必须在 select() 前调用,且对关联表完全无效——这是提速最直接、也最容易被绕过的一步。
主模型字段裁剪:只查要的,不查全量
TP8 默认走 SELECT *,哪怕你只用 id 和 nickname,也会把 content、remark 等大字段一并拉进来,内存翻倍、网络变慢。
正确做法是显式声明所需字段,且必须放在 select() 之前:
-
UserModel::field('id,nickname,email,create_time')->select()✅ 字符串方式,简单场景可用 -
UserModel::field(['id', 'nickname as name', 'status'])->select()✅ 数组方式,别名必须用键值对(如'nickname' => 'name'),混写'nickname as name'会失效 -
UserModel::field(true)->select()✅ 显式取全部字段,绕过模型自动补全逻辑,适合导出或同步 - 排除字段时,第二个参数传
true:UserModel::field(['content', 'remark'], true)->select()✅ 注意字段间不能有空格,'content, remark'会导致第一个字段未被排除
关联表字段裁剪:闭包里写 field(),别指望主模型链式调用
UserModel::with('profile')->field('id,profile.nickname') 这种写法不会生效,点号语法不被识别,profile.nickname 被当作未知列丢弃。
真正起作用的位置只有两个:关联方法定义内部,或 with() 的闭包中:
- 在
User模型里定义关联时固化裁剪:return $this->hasOne(Profile::class)->field(['user_id', 'nickname', 'avatar']);——user_id是外键,必须包含,否则关联数据绑定失败为null - 运行时动态控制:
UserModel::with(['profile' => function ($q) { $q->field(['user_id', 'nickname']); }])->select()✅ 每个关联闭包必须独立调用field() - 多层关联(如 User → Profile → Avatar)需逐级声明:
with(['profile' => function ($q) { $q->with(['avatar' => function ($qq) { $qq->field(['url', 'size']); }]); }])
为什么 field() 加了还是慢?检查三处硬伤
加了 field() 却没提速,大概率卡在底层 SQL 执行环节,不是 PHP 层问题:
- 外键没索引:比如
profiles.user_id没建索引,with('profile')就会触发全表扫描,EXPLAIN显示type=ALL - SQL 日志没开全:确认
config/log.php的'level'包含'sql',且config/database.php中'trigger_sql' => true和'sql_explain' => true都启用,否则看不到真实执行计划 - 用了
Db::table()或Db::name()直接查表:这类查询绕过模型,field()不生效;如需裁剪,得用$query->field(...)->select()形式
严格模式下字段类型转换与 field() 的隐性冲突
开启 declare(strict_types=1) 后,模型字段类型转换靠 $type 数组驱动,但这个机制只对查询结果生效,且依赖字段被实际查出。
如果你用 field(['id', 'status']),却在访问器里写了 getTotalPriceAttr($value, $data) 并依赖 $data['price'],而 price 没在 field() 列表里——访问器拿不到数据,计算直接崩或返回 null。
所以:field() 不只是性能开关,它也是数据可见性的边界。所有访问器、虚拟字段、业务逻辑依赖的字段,都得出现在 field() 列表中,否则就断链。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











