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