软删除在 laravel 5.5 中不会自动生效,必须同时满足模型引入 softdeletes trait 且数据库表存在 deleted_at 字段,缺一不可;否则 delete() 执行物理删除,数据直接消失。

软删除在 Laravel 5.5 中不会自动生效,必须同时满足两个条件:模型引入 SoftDeletes trait + 数据库表存在 deleted_at 字段,缺一不可;否则调用 delete() 就是硬删,数据直接消失。
模型里没加 SoftDeletes trait 会怎样
即使数据库有 deleted_at 字段,只要模型没声明 use SoftDeletes;,delete() 仍执行物理删除。Laravel 完全不识别该字段,也不会拦截删除逻辑。
- 常见错误现象:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'deleted_at' in 'where clause'—— 这其实是 trait 已启用但迁移缺失字段时的报错,反过来,没加 trait 却删成功了,说明根本没走软删流程 - Laravel 5.5 不支持自动推断软删除,
$dates或其他配置无法替代use SoftDeletes; - trait 必须写在模型 class 的花括号内第一行(或至少在
class X extends Model之后),不能只写use Illuminate\Database\Eloquent\SoftDeletes;而漏掉use SoftDeletes;
迁移里用 softDeletes() 和手动加字段的区别
$table->softDeletes() 是唯一推荐方式,它等价于 $table->timestamp('deleted_at')->nullable(),但还隐式注册了字段类型和索引行为;手动写后者可能遗漏底层适配。
- 新建表时:在建表迁移的
Schema::create()闭包里直接调用$table->softDeletes(); - 已有表追加:用独立迁移,
Schema::table('users', function (Blueprint $table) { $table->softDeletes(); }); - 别用
->nullableTimestamps()或->dateTime('deleted_at')->nullable()替代 —— Eloquent 在 5.5 中只认timestamp类型的deleted_at,否则restore()可能失败或时间格式错乱
withTrashed() 查询失效的典型原因
默认所有查询(find()、all()、where())都自动过滤 deleted_at IS NOT NULL 记录。但如果你发现 withTrashed() 没效果,大概率是链式调用位置错了。
- 正确:
User::withTrashed()->find(1)、User::withTrashed()->where('name', 'foo')->get() - 错误:
User::find(1)->withTrashed()(find()已执行并返回单个实例,再链withTrashed()无效) -
onlyTrashed()和restore()同理:必须在查询构建阶段调用,不能对已取出的模型实例调用 - Laravel 5.5 不支持在关系查询中直接用
withTrashed(),比如$user->posts()->withTrashed()->get()会报错,需改用Post::withTrashed()->where('user_id', $user->id)->get()
恢复或强制删除时容易忽略的细节
restore() 和 forceDelete() 行为与普通操作不同,且不触发全部生命周期事件。
-
$user->restore()会清空deleted_at,但不会触发updating或updated事件,只触发restoring和restored -
$user->forceDelete()绕过软删逻辑,直接执行 DELETE SQL,也不触发deleting/deleted,只触发forceDeleting/forceDeleted - 批量操作不支持
restore():没有restoreAll()方法,必须遍历或用原生 SQL 清空deleted_at - 如果模型定义了
$dates = ['deleted_at'],Laravel 5.5 会把该字段转成 Carbon 实例,但restore()后它仍是 Carbon 对象(值为null),不影响使用
最常被跳过的一步:确认迁移已执行且数据库字段真实存在。Laravel 5.5 不校验字段是否存在就尝试写入 deleted_at,报错才暴露问题,而静默硬删更危险——你得查日志或直接看数据库才能发现删没走软删流程。











