本文目录导读:

在 ThinkPHP 项目中实现数据还原与回收站功能,通常涉及软删除(逻辑删除)、备份还原以及中间表等核心概念,下面从这几个维度进行详细梳理并提供代码实现方案。
软删除(回收站的核心)
这是实现“回收站”最基础且最常用的技术,软删除的本质是不执行物理 DELETE,而是执行 UPDATE(设置 delete_time 字段)。
数据表设计
至少需要一个字段来标记是否被删除:
ALTER TABLE `user` ADD COLUMN `delete_time` INT UNSIGNED NULL DEFAULT NULL COMMENT '删除时间,NULL表示未删除';
模型定义
在 ThinkPHP 6/8 中,使用内置的软删除 Trait:
<?php
namespace app\common\model;
use think\Model;
use think\model\concern\SoftDelete;
class User extends Model
{
use SoftDelete; // 引入软删除功能
// 设置软删除字段(默认就是 delete_time,无需重复设置)
protected $deleteTime = 'delete_time';
// 开启自动时间戳(如果需要记录创建/更新时间)
protected $autoWriteTimestamp = true;
}
控制器中的 CRUD(回收站/还原演示)
<?php
namespace app\index\controller;
use app\common\model\User;
class UserController
{
// 1. 移入回收站(软删除)
public function recycle($id)
{
// 使用 destroy() 方法会触发软删除
$result = User::destroy($id);
// 或者:$user = User::find($id); $user->delete();
return json(['status' => $result ? 1 : 0]);
}
// 2. 查看回收站列表(只查被软删除的数据)
public function trashList()
{
// 使用 onlyTrashed() 方法查询被软删除的数据
$list = User::onlyTrashed()->select();
return json($list);
}
// 3. 还原数据(从回收站恢复)
public function restore($id)
{
// 找到被软删除的数据
$user = User::withTrashed()->find($id);
if ($user) {
// 使用 restore() 方法还原
$user->restore();
return json(['status' => 1]);
}
return json(['status' => 0, 'msg' => '数据不存在']);
}
// 4. 彻底删除(清空回收站)
public function forceDelete($id)
{
$user = User::withTrashed()->find($id);
if ($user) {
// force() 方法表示强制物理删除
$user->force()->delete();
return json(['status' => 1]);
}
return json(['status' => 0, 'msg' => '数据不存在']);
}
// 5. 正常查询列表(自动排除已删除的数据)
public function index()
{
// 常规查询会自动忽略软删除的数据
$list = User::select();
return json($list);
}
}
注意:如果涉及关联模型(如订单下的商品),需要同步配置关联的软删除,或者在业务层处理级联逻辑。
数据库备份与恢复(物理层面还原)
如果你指的是误删数据后的恢复(比如整个数据库表被清空),则可通过SQL 文件备份来实现。
使用 ThinkPHP 的 Db 类导出 SQL
以下是一个简单的备份控制器示例:
<?php
namespace app\admin\controller;
use think\facade\Db;
use think\response\Download;
class BackupController
{
// 数据库备份
public function backup()
{
// 读取所有表名
$tables = Db::getTables();
$sql = "-- ThinkPHP Database Backup\n";
$sql .= "-- Date: " . date('Y-m-d H:i:s') . "\n\n";
foreach ($tables as $table) {
// 生成建表语句 (MySQL)
$createSql = Db::query("SHOW CREATE TABLE `{$table}`");
$sql .= "-- 表结构: {$table}\n";
$sql .= $createSql[0]['Create Table'] . ";\n\n";
// 生成插入语句
$dataList = Db::table($table)->select();
foreach ($dataList as $data) {
$keys = implode('`, `', array_keys($data));
$values = implode("', '", array_map('addslashes', array_values($data)));
$sql .= "INSERT INTO `{$table}` (`{$keys}`) VALUES ('{$values}');\n";
}
$sql .= "\n";
}
// 保存为文件并下载
$filename = 'backup_' . date('Ymd_His') . '.sql';
file_put_contents($filename, $sql);
return download($filename, $filename);
}
// 数据库恢复 (上传SQL文件并执行)
public function restore()
{
// 假设通过请求接收文件 (upload)
$file = request()->file('file');
$sqlContent = file_get_contents($file->getRealPath());
// 分割SQL语句 (简单处理,根据分号分割)
$sqlArray = explode(";\n", $sqlContent);
Db::startTrans();
try {
foreach ($sqlArray as $sql) {
$sql = trim($sql);
if (!empty($sql)) {
Db::execute($sql); // 执行每条语句
}
}
Db::commit();
return json(['status' => 1]);
} catch (\Exception $e) {
Db::rollback();
return json(['status' => 0, 'msg' => $e->getMessage()]);
}
}
}
业务级“回收站”设计(复杂场景)
如果需要在回收站中区分不同类型的内容(比如文章、图片、分类),并支持按条件过滤,建议引入中间表或扩展字段管理。
表结构设计示例:
-- 删除记录表(回收站日志) CREATE TABLE `trash_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `model_name` varchar(50) NOT NULL COMMENT '模型名,如 article', `data_id` int(11) NOT NULL COMMENT '原数据ID', `delete_user` varchar(50) DEFAULT NULL COMMENT '删除人', `delete_reason` varchar(255) DEFAULT NULL COMMENT '删除原因', `original_data` text COMMENT '序列化存储的原始数据JSON', `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) );
操作流程:
- 删除时:除了进行模型软删除,同时将原始数据序列化存入
trash_items.original_data。 - 还原时:根据
model_name和data_id,反序列化数据,插入回原表,并清除回收站记录。 - 彻底清理:直接删除
trash_items记录。
这种方式更灵活,可以看到删除历史,也能防止不同模块的数据混淆。
总结与选择建议
| 场景 | 推荐方案 |
|---|---|
| 简单的文章/用户管理 | 使用模型软删除 (Trait SoftDelete) |
| 希望保留删除日志、原因 | 业务级 Log(中间表) |
| 数据库整体灾难恢复 | SQL 文件备份/恢复 |
| 缓存恢复(防止数据丢失) | 配合定时任务自动备份数据库 |
性能与安全提示:
- 软删除数据过多时,记得为
delete_time字段添加索引。 - 恢复 SQL 时,务必关闭外键检查
SET FOREIGN_KEY_CHECKS=0;,避免插入顺序导致失败。 - 回收站应定期清理(如超过30天的数据,通过计划任务执行彻底删除)。
如果你需要具体的原生 SQL 迁移或集成第三方备份插件(如 topthink/think-migration),可以告诉我你的具体版本(TP6 还是 TP8),我可以补充相应的代码细节。