软删除是Laravel通过SoftDeletes trait实现的逻辑层标记机制,依赖deleted_at字段自动过滤查询并覆盖delete()为更新操作。

软删除不是数据库层面的特性,而是 Laravel 通过 SoftDeletes trait 和约定字段协同实现的逻辑层标记机制。
SoftDeletes trait 做了什么
它不修改 SQL DELETE 行为,而是在模型上覆盖关键方法,把 delete() 变成更新 deleted_at 字段的操作,并自动在查询中添加 WHERE deleted_at IS NULL 过滤。
- 注入
$casts = ['deleted_at' => 'datetime'],确保字段被正确解析 - 重写
delete():调用runSoftDelete()执行update(['deleted_at' => now()]) - 重写
withTrashed()、onlyTrashed()等作用域方法,动态切换查询条件 - 在
bootSoftDeletes()中注册全局作用域(SoftDeletingScope),默认排除已软删记录
数据库字段必须是 deleted_at
Laravel 强依赖字段名 deleted_at(类型推荐 TIMESTAMP NULL 或 DATETIME NULL),不能随意改名。否则 trait 内部的字段引用和全局作用域都会失效。
- 迁移中必须显式添加:
Schema::table('users', function (Blueprint $table) { $table->softDeletes(); }); - 如果用的是 MySQL 5.6+,建议加索引提升查询性能:
$table->index('deleted_at'); - 手动建表时字段名写错(如
is_deleted或deleted_at_time)会导致软删除完全不生效,且无报错
查询时如何控制软删除行为
默认所有查询自动忽略软删记录;要查包含或仅查软删数据,必须显式调用对应方法:
-
Model::all()→ 自动加上WHERE deleted_at IS NULL -
Model::withTrashed()->get()→ 移除全局作用域,返回全部记录 -
Model::onlyTrashed()->get()→ 改为WHERE deleted_at IS NOT NULL -
Model::forceDelete()→ 跳过 soft delete,执行真实 DELETE - 关系查询(如
$user->posts)也受父模型软删除状态影响,需单独对关联模型调用withTrashed()
最容易被忽略的是:软删除字段一旦被设为非空(NOT NULL)、或默认值不为 NULL(比如设了 DEFAULT CURRENT_TIMESTAMP),就会导致 delete() 失败并抛出 SQL 错误——因为 trait 尝试写入 NULL 到一个不允许为空的字段。










