thinkphp软删除恢复不级联,需手动处理关联模型:先查onlytrashed()记录,再逐条restore(),注意字段名、类型、trait启用及事务包裹。

软删除后恢复数据,不会自动同步关联模型状态;ThinkPHP 默认不级联 restore(),必须手动处理关联项。
restore() 不触发关联模型的任何操作
调用 $user->restore() 只会清空当前模型的 delete_time 字段,订单、地址、日志等关联数据仍保持原状——哪怕它们也启用了 SoftDelete trait,也不会被连带恢复。
- 框架没有
withTrashed()或restore()的级联参数,protected $with = ['orders']仅影响查询,不影响恢复逻辑 - 如果你在
User模型里定义了hasMany('Order'),恢复用户时Order表的delete_time依然非空,查$user->orders仍为空(因默认过滤) - 想让关联数据一起“可见”,得单独对每个关联模型执行恢复动作,且必须先查出它们的软删记录
手动恢复关联数据的两种安全路径
不能依赖“一次 restore 全家幸福”,必须分步确认、分步恢复。关键点是:先查、再判、后调用。
- 恢复主模型后,立即用
onlyTrashed()查其关联软删记录:$user->orders()->onlyTrashed()->select() - 逐条调用关联模型实例的
restore(),并检查返回值:if (false === $order->restore()) { /* 记录失败 ID */ } - 若关联数据量大(如千条订单),改用
Db::name('order')->where('user_id', $user->id)->whereNotNull('delete_time')->update(['delete_time' => null]),但会跳过事件和验证 - 别在事务外操作:主模型恢复成功后,关联恢复失败会导致状态不一致;推荐包裹在
Db::transaction()中
在 restored 事件里自动拉取并恢复关联项
把关联恢复逻辑收口到模型事件中,避免每次业务代码重复写。但要注意事件作用域限制。
- 在模型类中启用事件:
protected $event = ['restored']; - 定义
restored()方法(注意命名是小驼峰):public function restored() - 方法体内不能直接访问请求上下文(如
Auth::id()),需提前把必要参数(如操作人 ID)传入模型或通过闭包捕获 - 示例片段:
$this->orders()->onlyTrashed()->chunk(100, function ($orders) { $orders->each->restore(); }); - 警惕递归风险:如果
Order恢复时也触发restored并反向恢复User,会造成死循环——加字段标记或条件判断规避
关联字段配置不一致导致恢复静默失败
主模型能恢复,关联模型却没动静,大概率是字段名或类型没对齐。ThinkPHP 对软删字段的识别非常严格。
- 主模型用
delete_time,但Order模型错配成deleted_at,且没声明protected $deleteTime = 'deleted_at';→restore()直接跳过 -
Order表的delete_time是BIGINT类型,但模型缺protected $type = ['delete_time' => 'integer'];→ 写入null被转成字符串,数据库存为'null',后续仍被判定为已删除 -
Order模型没启用SoftDeletetrait(只写了use think\model\concern\SoftDelete;,漏了use SoftDelete;)→ 方法不存在,调用静默失败 - 检查方式:打印
$order->getDeleteTime(),返回null才表示识别成功;否则说明字段未生效
最易忽略的是:即使所有配置都对,restored 事件里查关联数据时,若没加 onlyTrashed(),就等于在查“正常数据”,根本拉不到要恢复的记录。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











