ThinkPHP项目数据还原与回收站

wen PHP项目 3

本文目录导读:

ThinkPHP项目数据还原与回收站

  1. 软删除(回收站的核心)
  2. 数据库备份与恢复(物理层面还原)
  3. 业务级“回收站”设计(复杂场景)
  4. 总结与选择建议

在 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`)
);

操作流程:

  1. 删除时:除了进行模型软删除,同时将原始数据序列化存入 trash_items.original_data
  2. 还原时:根据 model_namedata_id,反序列化数据,插入回原表,并清除回收站记录。
  3. 彻底清理:直接删除 trash_items 记录。

这种方式更灵活,可以看到删除历史,也能防止不同模块的数据混淆。


总结与选择建议

场景 推荐方案
简单的文章/用户管理 使用模型软删除 (Trait SoftDelete)
希望保留删除日志、原因 业务级 Log(中间表)
数据库整体灾难恢复 SQL 文件备份/恢复
缓存恢复(防止数据丢失) 配合定时任务自动备份数据库

性能与安全提示

  • 软删除数据过多时,记得为 delete_time 字段添加索引。
  • 恢复 SQL 时,务必关闭外键检查 SET FOREIGN_KEY_CHECKS=0;,避免插入顺序导致失败。
  • 回收站应定期清理(如超过30天的数据,通过计划任务执行彻底删除)。

如果你需要具体的原生 SQL 迁移集成第三方备份插件(如 topthink/think-migration),可以告诉我你的具体版本(TP6 还是 TP8),我可以补充相应的代码细节。

抱歉,评论功能暂时关闭!