CI4软删除需手动实现:数据库表须有nullable的deleted_at字段,模型中必须配置$deletedField、$allowedFields(含deleted_at)和$primaryKey,并重写delete()方法;查询不自动过滤,需显式添加where条件。

CI4 的软删除不是配个属性就自动生效的——它必须手动实现,漏掉任一环节都会导致 delete() 仍走硬删、查询不自动过滤、甚至静默失败。
数据库表必须有 deleted_at 字段且类型正确
这是整个机制的地基。字段名必须严格为 deleted_at(小写+下划线),类型必须是 TIMESTAMP 或 DATETIME,且允许为 NULL:
- 迁移中写:
$table->timestamp('deleted_at')->nullable();—— 不要用date、integer或tinyint,否则时间比较和自动填充会出错 - 已有表请手动执行:
ALTER TABLE users ADD deleted_at DATETIME NULL; - 字段名写错(比如写成
is_deleted或deleted)会导致后续所有逻辑失效,模型完全无法识别
模型里三个配置项缺一不可
CI4 模型不会自动扫描字段,必须显式声明语义:
-
protected $deletedField = 'deleted_at';—— 告诉模型哪个字段承担软删标记作用 -
protected $allowedFields = ['name', 'email', 'deleted_at'];—— 必须包含deleted_at,否则update()无法写入该字段,软删操作会静默返回false -
protected $primaryKey = 'id';—— 主键必须准确,否则$model->delete($id)无法定位行,可能误更新整表
delete() 方法必须重写,不能依赖默认行为
CI4 的 Model::delete() 默认就是物理删除。要改成软删,必须在模型中覆盖:
public function delete($id = null, bool $purge = false)
{
if ($purge === true) {
return parent::delete($id);
}
$data = [$this->deletedField => date('Y-m-d H:i:s')];
return $this->update($id, $data);
}
- 加
$purge参数是为了保留强制物理删除的能力(如 GDPR 清理场景) - 务必检查
$id是否传入:不传$id直接调用$this->update(null, $data)会更新全表,这是 CI4update()的经典陷阱 - 不要只写
$this->update($id, [$this->deletedField => time()])——time()是整数,而字段是DATETIME,会导致写入0000-00-00 00:00:00或报错
查询时不会自动过滤,得自己加条件或封装
CI4 不像 Laravel 那样自动在所有 find()、findAll() 中追加 WHERE deleted_at IS NULL。这意味着:
- 查正常数据:必须显式写
$model->where('deleted_at IS NULL')->find($id)或封装一个findActive()方法 - 查已删除数据:用
$model->where('deleted_at IS NOT NULL')->findAll() - 恢复数据:直接
$model->update($id, [$this->deletedField => null]),注意传null而非空字符串或0 - 如果用 Query Builder 绕过模型(如
$this->db->table('users')->get()),软删逻辑完全不生效,需手动加条件
最容易被忽略的是 $allowedFields 和 $primaryKey —— 它们不出错不报警,但一旦缺失或错配,软删就会“看起来成功”,实际没写入字段,或删错了行。上线前务必用真实 ID 测试单条软删 + 查询验证。


















