理Think怎么使模型字段延迟加载开关 Think手动控制是否触发关联教程|Duuu笔记
PHP进阶技巧:本文深入解析
ThinkPHP模型需用field()显式指定字段关闭自动加载,关联查询需用hasWhere()实现主表过滤,$lazy=false仅禁用未with的隐式关联触发。
ThinkPHP 模型中如何关闭字段自动加载
默认情况下,ThinkPHP 的模型会把所有字段都查出来,哪怕你只想要
id
和
name
。这不是性能问题,是设计逻辑问题——它不区分“需要的字段”和“定义的字段”。想关掉?不能靠配置项全局关,得在查询时手动指定。
用
field()
显式声明要查的字段,比如
$user->field('id,name')->find(1)
,没列进去的字段不会出现在结果里,也不会被赋值到模型属性中
如果用了
with()
关联,
field()
只影响主表,关联表字段仍按关联定义加载;想控制关联字段,得进关联方法里改
field()
别用
hidden
或
visible
属性替代——那只是序列化时过滤,数据库照样全查,内存里也照存
ThinkPHP 关联查询时怎么阻止自动触发
调用
$user->posts
这种属性访问,会立刻触发关联查询,哪怕你后面根本没用到
posts
数据。这不是懒加载,是“伪懒加载”:只要访问就查,无法跳过。
真正能关掉的只有
with()
之外的隐式访问:给模型加
protected $lazy = false;
(TP6.1+),这样
$user->posts
不再自动查,但注意——这仅对“未显式 with 的关联”生效
已经写了
with('posts')
,那就一定会查,
$lazy = false
对它无效
如果你只是想“查但不立刻执行”,得用
load()
替代属性访问,比如
$user->load('posts')
,它返回模型本身,且可链式调用,但依然会触发查询
为什么 setAttr 不影响字段加载时机
有人试过在模型里重写
setAttr
或用
getAttr
拦截字段,发现完全没用——因为字段加载发生在数据从数据库取出、赋值给模型属性的阶段,远早于任何 getter/setter 调用。
setAttr
是给“写入时的数据处理”用的,比如加密、格式转换,跟“查哪些字段”无关
字段是否被加载,取决于查询构造器(
Query
)传了什么
field
参数,以及关联关系是否被标记为“需预载入”
想动态决定字段,得在调用前拼
field()
,而不是寄希望于模型内部钩子
TP6 中 with() 的 where 条件写在哪最容易踩坑
很多人以为
with(['posts' => function ($q) { $q->where('status', 1); }])
能过滤关联数据,结果发现主模型还是全量返回,只是关联数据少了——这没错,但容易误以为主表也被过滤了。
“
PHP免费学习笔记(深入)
”;
这个
where
只作用于关联表的
SELECT
,不影响主表查询条件,也不影响主模型实例数量
如果想让主表也受关联条件影响(比如只查有已发布文章的用户),必须用
hasWhere()
,例如
UserModel::hasWhere('posts', ['status' => 1])->select()
with()
+ 闭包里的
limit()
有效,但
order()
在部分版本中会被忽略,建议统一在关联定义里写
order()
延迟加载不是开关一按就完事,ThinkPHP 把“字段选择”“关联触发”“条件下推”拆成三套机制,各自生效时机不同。最常被忽略的是:你以为关掉了某个功能,其实只是换了个地方执行而已。
