软删除需同时具备SoftDeletes trait和deleted_at字段,否则delete()为硬删或报错;restore()仅对软删除记录有效,且默认不触发事件,查询默认忽略软删除数据。

软删除必须加 SoftDeletes trait 和 deleted_at 字段
不加这个 trait,调用 delete() 就是真删;加了但没加数据库字段,会报错或静默失败。Laravel 的软删除本质是给记录打个时间戳标记,不是改逻辑。
- 模型里必须
use Illuminate\Database\Eloquent\SoftDeletes;并在 class 声明中加上use SoftDeletes; - 数据表必须有
deleted_at字段,类型为TIMESTAMP NULL(MySQL)或datetime null(SQLite/PostgreSQL) - 如果字段名不是
deleted_at,得在模型里显式定义:protected $dates = ['deleted_at'];并重写getDeletedAtColumn()方法
restore() 只对已软删除的记录有效,且默认不触发事件
调用 restore() 后查不到数据?大概率是这条记录根本没被软删除过——比如之前是硬删,或者 forceDelete() 过,那它就彻底没了,restore() 无效。
-
restore()不会触发restoring/restored事件,除非你手动开启:在模型里加protected $dispatchesEvents = ['restored' => Restored::class]; - 批量恢复要用
withTrashed()->where(...)->restore(),直接Model::where(...)->restore()会报错,因为没查到软删除数据 -
restore()不会自动清理关联模型的deleted_at,父子关系需自行处理
查询时默认忽略软删除数据,绕过要主动声明
你查不到“已删除”的记录,不是代码写错了,是 Laravel 默认行为。它会在所有 SELECT 语句里自动加 WHERE deleted_at IS NULL。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 查全部(含软删):用
withTrashed(),例如User::withTrashed()->find(123) - 只查软删的:用
onlyTrashed(),例如User::onlyTrashed()->where('email', 'x@y.z')->first() - 关联查询也受限制:如果父模型用了软删除,
$user->posts默认不会返回已软删的 posts,得在关系方法里加->withTrashed()
forceDelete() 是不可逆的硬删,慎用
一旦执行 forceDelete(),数据从数据库物理消失,restore() 完全无效。这不是“更彻底的删除”,而是放弃软删除机制的明确选择。
- 常见误操作:在控制器里写
$model->delete()后又想restore(),结果发现没生效——其实是之前某处调过forceDelete()或迁移时漏建deleted_at字段 - 软删除状态下,
delete()等价于touch('deleted_at');而forceDelete()会跳过所有软删除逻辑,走原生DELETE FROM - 测试环境容易忽略这点:seeders 里如果用了
forceDelete(),后续restore()相关测试必然失败
真正麻烦的不是怎么删,而是删完之后谁在什么时候、以什么方式查哪部分数据。软删除让「删除」变成一个状态,而状态管理最容易出问题的地方,永远在边界条件和默认行为上。










