一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

为什么 TP6.0 模型修改器不生效:数据类型与赋值时机剖析 源码

时间:2026-07-10 10:10:03 编辑:袖梨 来源:一聚教程网

模型修改器仅在模型的save()、create()、update()等方法中触发,Db::insert()或原生SQL插入时不生效;setFieldAttr签名须为public function setFieldAttr($value, $data = null),且$type转换优先于修改器执行。

模型修改器只在模型的 save()create()update() 等方法中触发,直接调用 Db::insert() 或原生 SQL 插入时完全不生效。

修改器只对模型写入方法有效

这是最常被忽略的前提。TP6 的修改器(setFieldAttr)是模型层的逻辑,不是数据库驱动或查询构建器的功能。

  • ✅ 生效场景:UserModel::create(['email' => '[email protected]'])$user->name = 'x'; $user->save();
  • ❌ 不生效场景:Db::name('user')->insert(['email' => '[email protected]'])$model->data($data)->isUpdate(false)->save();(绕过模型校验与修改器流程)
  • ⚠️ 注意:即使用了模型实例,但手动调用 ->insert()(如 $model->insert($data))也不会触发修改器——因为那是底层 Query 操作,跳过了模型的 save() 生命周期

setFieldAttr 函数签名与参数陷阱

修改器函数必须严格匹配签名 public function setFieldAttr($value, $data = null),否则框架无法识别或调用失败。

  • $value 是你传入的原始值(比如 '[email protected]'),不是数据库字段当前值
  • $data 是当前要写入的完整数据数组(仅在 save() 时存在;create() 中可能为空)
  • 常见错误:漏掉第二个参数声明(function setEmailAttr($value)),导致 PHP 7+ 严格模式下报 Declaration must be compatible 警告,修改器静默失效
  • 示例正确写法:
    public function setEmailAttr($value, $data = null){    return strtolower($value);}

类型转换($type)与修改器的执行顺序冲突

如果模型同时定义了 $typesetFieldAttr,TP6 会先做类型转换,再进修改器——这意味着你拿到的 $value 可能已是转换后的类型,而非原始字符串。

  • 例如:
    protected $type = ['price' => 'integer'];
    +
    public function setPriceAttr($value) { return $value * 100; }
    → 传入 '19.99' 会被先转成整型 19,再进修改器变成 1900,而非预期的 1999
  • 解决办法:要么移除 $type,改由修改器统一处理(推荐);要么在修改器里手动还原原始输入(需配合 getData() 或前置缓存)
  • 验证方式:在修改器内 dump($value); die;,确认它是不是你认为的“原始值”

replace(true) 和修改器毫无关系

有用户误以为 replace(true) 是开启修改器的开关,其实它只影响 SQL 类型(REPLACE INTO vs INSERT INTO),和模型逻辑完全解耦。

  • $model->replace(true)->save($data) 会触发修改器(因为仍是 save()
  • $model->replace(true)->insert($data) 不会触发修改器(因为走的是底层 insert()
  • 源码佐证:在 think/db/Mysql::insert() 中,$replace 参数只用于拼 SQL 字符串,不参与模型事件分发

真正决定修改器是否运行的,是「是否经过模型的 save() 流程」,而不是某个布尔配置项或 SQL 关键字。只要绕开模型写入入口,哪怕写满 10 个 setXxxAttr,也只会安静地躺在那里,一动不动。

热门栏目