当前位置:首页 > CMS教程 > 其它CMS > 列表

LaravelModel模型_软删除SoftDeletes功能使用【详解】

发布:smiling 来源: PHP粉丝网  添加日期:2026-05-23 17:54:23 浏览: 评论:0 

软删除本质是标记而非删除,需模型启用SoftDeletes trait且数据库存在匹配类型的deleted_at字段;trashed()是判断软删状态的唯一可靠方法,withTrashed()和onlyTrashed()用于显式查询,restore()失败多因字段配置或事务问题。

软删除不是“删了还能找回来”,而是压根没删——只是给记录打了个 deleted_at 时间戳标记。只要模型正确启用 SoftDeletes,且数据库字段存在、类型匹配,后续所有行为才可预期。

怎么确认一条记录是否已被软删除

不能靠肉眼看数据库字段,也不能依赖 find() 是否返回结果——因为默认查询会自动过滤掉软删数据。

$user->trashed() 是唯一可靠判断方式:返回 true 表示该模型实例的 deleted_at 非空

必须先查出模型实例(哪怕已软删),再调用该方法。例如:User::withTrashed()->find(123)->trashed()

如果直接用 User::find(123),返回 null 时你根本不知道是“不存在”还是“已被软删”

注意:trashed() 是实例方法,不能在查询构造器上链式调用(如 User::where(...)->trashed() 会报错)

查不到软删记录?先看 withTrashed() 和 onlyTrashed() 用对没

这是最常卡住人的地方:默认所有 Eloquent 查询都带 WHERE deleted_at IS NULL 全局作用域。

User::all()、User::find(1)、User::where(...)->get() —— 全都不含软删数据

要查“全部(含软删)”,必须加 withTrashed():User::withTrashed()->where('email', 'x@y.z')->get()

要查“仅软删的”,必须加 onlyTrashed():User::onlyTrashed()->where('name', 'test')->first()

onlyTrashed()->find(1) 返回 null?不是代码错,而是这条记录根本没被软删过(可能硬删了,或 delete() 根本没执行成功)

restore() 失败却没报错?重点排查这三处

$user->restore() 看似简单,但失败时往往静默吞掉异常,导致你以为恢复成功了,其实 deleted_at 还在库里。

检查数据库字段是否被设为 NOT NULL(比如误加了 DEFAULT CURRENT_TIMESTAMP),导致 UPDATE 时无法写入 NULL

检查模型事件钩子:restoring 或 restored 中是否有逻辑抛出异常或返回 false,中断了流程。

事务中调用后没 commit,或者外层事务被 rollback,查库时自然看不到变化

批量调用 User::onlyTrashed()->where(...)->restore() 不会触发单个模型的 restored 事件,需手动遍历 + 实例调用

自定义 deleted_at 字段名和类型兼容性

别只改 DELETED_AT 常量就完事,Laravel 对字段类型和解析逻辑很敏感。

自定义字段名必须配合 protected $dates = ['my_deleted_at'],否则 restore() 可能清不掉值

Laravel 8+ 要求 deleted_at 必须是 TIMESTAMP NULL,用 DATETIME 会导致 restore() 失败且无提示

如果在 $casts 里写了 'deleted_at' => 'date',会覆盖框架内置的时间处理逻辑,破坏 trashed() 判断

迁移中务必用 $table->softDeletes(),而不是手写 $table->timestamp('deleted_at')->nullable()(虽等效,但少了一层兼容性保障)

真正容易被忽略的是:软删除生效的前提不是“用了 trait”,而是“trait + 字段存在 + 字段类型正确 + 没被其他配置意外覆盖”。任何一环断开,trashed() 就可能永远返回 false,而你还在奇怪为什么删不掉数据。

Tags: LaravelModel 软删除SoftDeletes

分享到: