跳转到主内容
websoft网络软件专家 - 深耕网络技术,打造实用软件!

PHP怎么使用Eloquent Attribute Query States属性查询状态_Laravel CQRS查询模型【技巧】

Eloquent无“Attribute Query States”概念,本质是通过getAttribute(走accessor/cast)、getOriginal(原始快照)、getRawOriginal(未cast原始值)等方法组合判断属性来源与变化轨迹。 PHP中Eloquent的
getAttribute
和
getOriginal
怎么区分状态? 直接说结论:Eloquent没有叫「Attribute Query States」的内置概念,所谓“属性查询状态”实际是开发者对
getAttribute
、
getOriginal
、
isDirty
、
wasChanged
等方法组合使用的俗称。它本质是判断一个模型属性当前值的来源与变化轨迹——比如是从数据库读的、刚被
setAttribute
改过的、还是经过
cast
或
accessor
处理过的。 常见错误现象:
$user->name
返回的是修改后值,但
$user->getOriginal('name')
还是旧值,有人误以为这是“缓存未刷新”,其实是Eloquent明确记录了原始快照。
getAttribute('name')
:走accessor → cast → 值转换流程,返回最终对外暴露的值
getOriginal('name')
:只读取构造时从数据库加载的原始值(不含cast,也不触发accessor)
getRawOriginal('name')
:连cast都不走,返回DB里原样存的值(比如JSON字段存的是字符串
"{\"a\":1}"
) 调用
fresh()
会重置
original
,但不会清空
changes
数组;
syncOriginal()
才是手动同步原始值 Laravel CQRS里怎么避免在Query Model中误用
save()
或
setAttribute()
? CQRS要求Query Model只读,但Eloquent模型默认可写。如果把一个用于查询的
UserReadModel
实例不小心调了
save()
,它真会发UPDATE语句——哪怕你只打算读数据。 使用场景:做报表、后台列表、API只读接口时,应切断写能力,而非靠文档或约定来约束。 立即学习 “ PHP免费学习笔记(深入) ”; PHP 8.5.5 PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。 下载 最简单做法:继承
Illuminate\Database\Eloquent\Model
后重写
save()
、
delete()
、
update()
等方法,统一抛出
RuntimeException
更彻底的方式:不继承Eloquent Model,改用
DB::table('users')->select(...)->get()
+
stdClass
或自定义DTO类,完全脱离ORM生命周期 若仍需Eloquent语法糖(如
withCount
),可用
new static
构造空白模型再
setRawAttributes()
注入查询结果,确保
$model->exists === false
,这样
save()
会强制走INSERT而非UPDATE 为什么
isDirty('status')
在
retrieved
事件后立刻为
true
? 这不是bug,是Eloquent设计使然:当模型从数据库加载完成触发
retrieved
事件时,
original
已设好,但
attributes
和
original
此时完全一致,
isDirty()
应为
false
。如果你观察到
true
,大概率是以下原因之一: 某个
accessor
或
mutator
在
retrieved
钩子中偷偷调了
setAttribute('status', ...)
,导致
changes
被写入 字段用了
cast
(比如
'status' => 'integer'
),而DB里存的是字符串
'1'
,cast后变成整数
1
,Eloquent认为值“变了”(类型不同) 数据库字段默认值被ORM解析成
null
,但实际DB返回的是空字符串
''
,cast后不等价,触发dirty标记 验证方式:在
retrieved
事件回调里打日志,输出
$model->getAttributes()
和
$model->getOriginal()
对比结果。 Query Model里用
whereHas
查关联,性能掉得厉害怎么办?
whereHas
底层是LEFT JOIN + 子查询或EXISTS,当关联表数据量大、没建对索引时,很容易全表扫描。CQRS场景下尤其敏感,因为Query Model本就不该承担复杂关系计算。 优先用
whereExists
替代
whereHas
,它生成EXISTS子查询,通常比JOIN更轻量 给外键字段加索引:比如
posts.user_id
必须有索引,否则
whereHas('user')
必然慢 避免嵌套
whereHas
(如
whereHas('author.posts.tags')
),拆成多次查询+内存过滤更可控 考虑冗余字段:在主表加
has_published_posts
布尔字段,用Observer监听Post状态变更时更新,查询时直接
where('has_published_posts', true)
真正难处理的不是语法,而是忘记Query Model的边界——它不该替Domain Model做业务规则判断,比如“用户是否满足VIP条件”这种逻辑,应该由Domain Service算好后写入专门的查询视图或物化字段。

相关文章