ThinkPHP 的 restore() 触发 restoring(恢复前,返回 false 可中断)和 restored(恢复后,仅当 delete_time 确实置为 NULL 时触发);需在模型中配置事件或绑定监听器;restoring 中可校验权限或业务规则并返回 false 阻止非法恢复;restored 中宜用 saveAll 批量恢复关联数据;所有操作应包裹在 Db::transaction 中确保事务一致性。
ThinkPHP 本身没有“事件驱动的
数据恢复
”机制,所谓“事件做数据恢复”,其实是把模型事件(如
、
)和软删除恢复逻辑配合使用,用来在恢复前后插入校验、日志、关联处理等动作——它不替代恢复本身,只是增强恢复过程的可控性。
restore() 触发哪些模型事件
调用
时,ThinkPHP 会按顺序触发:
:恢复前触发,可在此中断恢复(返回
)
:恢复成功后触发,仅当数据库字段真实更新为
才触发
注意:
和
是模型实例事件,不是静态方法或全局钩子;必须在模型类中定义
或使用
绑定监听器。
怎么在 restoring 中阻止非法恢复
比如只允许管理员恢复、或禁止恢复已过期的记录,可在
回调里加判断:
立即学习
“
PHP免费学习笔记(深入)
”;
关键点:
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
返回
会直接终止本次
,且不报错,只静默失败
是原始字段值,不是 PHP 时间戳,需手动转换
事件里不能用
,否则整个事务会崩,且
返回
而非异常
restored 事件里怎么安全处理关联数据
软删除默认不级联,但你可以在
里主动拉取并恢复关联项:
注意:
别在
里调
—— 关联模型没启用
trait 的话会报错
用
批量更新比循环调
更高效,也避免触发重复事件
如果关联模型也有
事件,它们会各自触发,无需额外注册
事件 + 事务恢复时最容易漏掉的一件事
模型事件默认不在事务内自动回滚。比如你在
里写了一条日志到另一张表,但主恢复因数据库约束失败了,那条日志仍会留在库里。
正确做法是:手动开启事务,并确保事件里的所有操作都共享同一连接:
否则,哪怕
成功,后续事件里的 DB 操作失败,也不会回滚主记录的恢复状态——这是线上恢复出错后数据不一致的常见根源。
restoringrestored$model->restore()restoringfalserestoredNULLrestoringrestoredprotected $event = [...]observe()restoringpublic function restoring()
{
if (!auth('admin')->check()) {
return false; // 中断恢复
}
if ($this->delete_time && strtotime($this->delete_time) < time() - 86400 * 30) {
Log::warning('attempt_restore_expired', ['id' => $this->id]);
return false;
}
}falserestore()$this->delete_timethrow new Exception()restore()falserestoredpublic function restored()
{
// 恢复订单下的所有订单项
$this->items()->onlyTrashed()->chunk(100, function ($items) {
foreach ($items as $item) {
$item->delete_time = null;
}
OrderItem::saveAll($items);
});
}restored$this->items()->restore()SoftDeletesaveAll()restore()restoredrestoredDb::transaction(function () use ($user) {
if (false === $user->restore()) {
throw new Exception('Restore failed');
}
// 日志、关联更新等都在同一事务里
});restore()