应使用 yii\db\expression 配合迁移定义数据库原生时间戳,默认 current_timestamp 及 on update current_timestamp,并在模型 beforesave() 中 clearattributes 跳过 php 赋值。

在 Yii 2.0.29 中,若需让数据库(如 MySQL)自动生成时间戳(例如 created_at 或 updated_at),而不是由 PHP 层赋值,应使用 yii\db\Expression 配合模型的 beforeSave() 方法或迁移中的字段定义。关键在于:**避免 PHP 生成时间、交由数据库处理,同时确保 ActiveRecord 正确识别该字段不参与批量赋值**。
在迁移中定义原生时间戳字段
创建表或修改字段时,显式指定数据库默认值和自动更新行为:
- MySQL 中用
CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP - 使用
$this->timestamp()辅助方法仅适用于简单场景,但不支持自动更新;更稳妥的是直接写 SQL 类型
示例迁移代码:
$this->createTable('post', [
'id' => $this->primaryKey(),
'title' => $this->string()->notNull(),
'created_at' => $this->dateTime()->defaultValue(new \yii\db\Expression('CURRENT_TIMESTAMP')),
'updated_at' => $this->dateTime()->defaultValue(new \yii\db\Expression('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')),
]);
注意:defaultValue(new Expression(...)) 会将表达式原样写入 CREATE TABLE 语句,数据库真正接管初始化与更新逻辑。
在模型中禁用 PHP 层时间赋值
若字段已由数据库自动维护,应防止 ActiveRecord 在保存时覆盖它:
- 从
safe或required场景中移除created_at/updated_at - 重写
beforeSave(),对新记录/更新记录跳过手动赋值
示例:
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
// 不设置 created_at / updated_at,交由数据库处理
// 可选:只清除 dirty 属性,避免被 save() 写入
if ($insert) {
$this->clearAttributes(['created_at', 'updated_at']);
} else {
$this->clearAttributes(['created_at']); // 仅更新 updated_at 由 DB 自动完成
}
return true;
}
return false;
}
查询时正确读取时间戳值
数据库返回的时间戳会被 Yii 自动转为 PHP DateTime 对象(依赖 typecast 行为)。确保:
- 字段类型在数据库中是
DATETIME或TIMESTAMP(非字符串) - 模型中未定义
createdAt等 getter/setter 干扰原始值 - 如需格式化输出,用
Yii::$app->formatter->asDatetime($model->created_at)
验证与调试技巧
确认是否生效:
- 执行
INSERT INTO post (title) VALUES ('test')后查表,看created_at是否有值且为当前时间 - 执行
UPDATE post SET title='new' WHERE id=1,检查updated_at是否自动变更 - 开启 Yii 日志中的 DB 查询日志,观察 INSERT/UPDATE 语句是否包含
created_at字段 —— 它不应出现
不复杂但容易忽略:Expression 必须在迁移中定义默认值,模型层只需“放手”,而非“主动设置”。











