软删除是通过deleted_at字段标记而非物理删除,restore()失效主因是未查到记录或模型/字段配置不全:需同时启用softdeletes、添加nullable timestamp/datetime字段、正确配置$casts或$dates。

软删除不是“删了还能找回来”,而是根本没删——只是加了个 deleted_at 时间戳标记;restore() 失效,90% 是因为压根没查到那条记录,或模型/字段没配对。
模型和数据库字段必须同时到位
缺一不可:模型里 use SoftDeletes,表里有 deleted_at 字段,且类型为 TIMESTAMP NULL 或 DATETIME NULL(MySQL 下不能是 NOT NULL DEFAULT CURRENT_TIMESTAMP)。
-
$table->softDeletes()在迁移里执行,等价于$table->timestamp('deleted_at')->nullable() - 字段名不是
deleted_at?得重写getDeletedAtColumn(),并确保$casts里声明'deleted_at' => 'datetime' - 漏掉
$casts会导致restore()静默返回false,不报错也不生效 - 用
protected $dates = ['deleted_at']替代$casts也行,但 Laravel 8+ 更倾向$casts,且别写成'deleted_at' => 'date'(会破坏恢复逻辑)
查不到软删记录?默认就故意忽略
User::find(1)、User::where(...)->get() 全部自动加 WHERE deleted_at IS NULL。这不是 bug,是设计行为。
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
- 查“所有数据(含已软删)”:用
User::withTrashed()->find(1) - 只查“已被软删的”:用
User::onlyTrashed()->where('email', 'x@y.z')->first() -
User::onlyTrashed()->find(1)返回null?说明这条记录没被软删过——可能被forceDelete()物理删了,或delete()根本没执行成功 - 关联查询也受限制:
$user->posts默认不包含已软删的 posts,关系方法里得手动加->withTrashed()
restore() 必须在带 deleted_at 的实例上调用
User::restore() 会直接报错;User::find(1)->restore() 永远无效——因为 find(1) 根本拿不到软删数据。
- ✅ 正确:
$user = User::withTrashed()->find(123); $user->restore(); - ✅ 批量安全方式:
User::onlyTrashed()->where('team_id', 5)->get()->each->restore();(触发restoring/restored事件) - ⚠️ 高性能但跳过逻辑:
User::onlyTrashed()->where('team_id', 5)->update(['deleted_at' => null]);(不触发事件、访问器、修改器) -
restore()返回bool:true表示成功(该记录确实在软删状态),false表示找不到或已硬删;要用异常捕获就改用restoreOrFail()
硬删后无法恢复,级联恢复得手动处理
forceDelete() 是物理删除,执行的是原生 DELETE FROM,数据从表里彻底消失。restore() 对它完全无效。
- 想“彻底删掉某条软删记录”?先判断:
if ($user->trashed()) { $user->forceDelete(); } -
restore()不会自动恢复关联模型(比如 Post 恢复了,Comment 仍软删);要级联,得手动查Comment::onlyTrashed()->where('post_id', $post->id)->restore() - 事务中调用
restore()后未提交,或外层 rollback,会导致查库时deleted_at还在——表面失败,实则是事务没落库 - 自定义主键(如
protected $primaryKey = 'uuid')时,别用数字 ID 查询,onlyTrashed()->find(1)肯定返回null
最常卡住的地方不是代码写错,而是:查的时候没加 withTrashed(),字段类型设成 NOT NULL,或者误把 forceDelete() 当成普通删除来用。软删除本质是状态标记,不是开关——状态错了,恢复就无从谈起。










