应通过模型的beforewrite方法动态注入用户id,而非数据库default或$auto闭包;需控制器显式传入user_id,beforewrite中用isinsert()/isupdate()判断操作类型并安全赋值created_by/updated_by。

模型字段默认值不能写死在数据库里怎么办
ThinkPHP 的模型字段默认值如果硬编码在数据库 DEFAULT 里,就无法根据当前登录用户动态生成(比如 created_by 自动填当前用户 ID)。数据库层做不到的事,得交回 PHP 层控制。
核心思路是:**不在数据库设 DEFAULT,改用模型的 save() 前钩子或自动完成机制**。
-
protected $auto数组只对新增数据生效,且不支持闭包,没法取到session或Auth - 更可靠的是重写模型的
beforeWrite方法,在数据写入前注入值 - 注意:这个钩子对
update()也触发,要加判断只处理新增
怎么在 beforeWrite 里安全获取当前用户 ID
直接调 session('user_id') 或 Auth::id() 看似简单,但模型层不该强依赖应用层的认证逻辑——万一以后换 JWT、换多端登录,这里就得改。
推荐做法是把用户上下文“传进来”,而不是“自己去拿”:
- 控制器里创建模型实例时,显式传参:
new UserLog(['user_id' => Auth::id()]) - 或在
beforeWrite中检查$this->user_id是否已存在,有则用,无则跳过(避免空指针) - 别在模型里调
Auth::check()—— 模型没权限感知能力,容易在命令行或队列中报错
自动填充 created_by 和 updated_by 的典型写法
这两个字段常见但极易踩坑:比如 updated_by 被误设成创建人,或者更新时没刷新。
protected function beforeWrite($data)
{
if ($this->isInsert()) {
$data['created_by'] = $this->user_id ?? 0;
}
if ($this->isUpdate()) {
$data['updated_by'] = $this->user_id ?? 0;
}
return $data;
}
-
isInsert()和isUpdate()是 ThinkPHP 6+ 提供的判断方法,比查主键是否存在更准 - 用
?? 0而不是?: 0,防止null或false被误判 - 不要在
beforeWrite里调$this->save()或其他写库操作,会引发递归
为什么不用 create() 静态方法配 auto 完成
很多人试过 protected $auto = ['created_by' => 'getUserId'],结果发现 getUserId 方法根本没执行 —— 因为 $auto 只支持字符串、数组、闭包,但闭包在模型定义时就固化了,拿不到运行时的用户上下文。
- ThinkPHP 6.1+ 支持闭包写法:
['created_by' => function () { return Auth::id(); }],但依然违反模型职责分离原则 - 闭包在模型类加载时就被解析,
Auth可能尚未初始化,线上环境容易报Call to a member function id() on null - 更隐蔽的问题:单元测试时
Auth为空,这个闭包直接崩,而beforeWrite至少能兜底
真正难的不是写几行代码,而是想清楚“谁该负责提供用户身份”——模型只管结构和规则,用户上下文必须由上层明确传递。漏掉这层设计,后面所有默认值都会变成定时炸弹。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










