ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?

thinkphp实现软删除需在数据表添加delete_time字段用于标记删除状态;2. 模型中引入softdelete trait并配置相关字段即可自动将delete()转为更新操作;3. 查询时默认自动排除软删除数据,可用withtrashed()或onlytrashed()控制是否包含已删除数据;4. 恢复数据调用restore()方法即可将delete_time置空;5. 彻底删除可调用force()或delete(true)执行物理删除,确保灵活性与数据安全。

ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?

在ThinkPHP里实现软删除,核心思路其实很简单:我们不在数据库里真正删掉那条数据,而是给它打个“已删除”的标记。通常的做法是,在数据表里加一个 delete_time 字段,当需要“删除”时,就往这个字段里填上当前时间戳,而不是执行 DELETE 语句。这样一来,数据还在,只是被系统逻辑上“隐藏”了。ThinkPHP的ORM对这个功能有非常好的内置支持,用起来非常顺手。

ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?

解决方案

要让你的ThinkPHP应用支持软删除,你需要做几件事。首先,数据库层面得有个字段来承载这个“删除标记”。我通常会用 delete_time,类型设为 DATETIMETIMESTAMP,并且允许为空(NULL)。当数据正常存在时,这个字段就是 NULL;一旦被“删除”,就会被填充上删除时的时间。

接下来是模型层的配置。在你的模型类里,你需要引入 SoftDelete Trait,并指定你用来标记删除的字段名。比如,如果你的模型是 appmodelUser

立即学习“PHP免费学习笔记(深入)”;

ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?

<?phpnamespace appmodel;use thinkModel;use thinkmodelSoftDelete; // 引入软删除Traitclass User extends Model{    // 使用软删除Trait    use SoftDelete;    // 定义软删除字段,默认是delete_time,如果你的字段名不同,需要在这里指定    // protected $deleteTime = 'delete_time'; // 如果字段名就是delete_time,可以省略这行    // 定义时间戳字段名,如果你的表里有create_time和update_time,通常也需要定义    protected $createTime = 'create_time';    protected $updateTime = 'update_time';    // 如果你的主键不是id,也要在这里指定    // protected $pk = 'user_id';}

完成这些配置后,当你对模型实例调用 delete() 方法时,ThinkPHP就不会真的去执行 DELETE FROM table WHERE id = xxx 了。它会悄悄地执行一个 UPDATE table SET delete_time = NOW() WHERE id = xxx 的操作。这感觉就像是给数据贴了个隐形标签,告诉系统:“嘿,这玩意儿暂时别显示了。”

比如,你想“删除”ID为1的用户:

ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?

use appmodelUser;$user = User::find(1);if ($user) {    $user->delete(); // 此时,user表的id为1的记录的delete_time字段会被填充    echo "用户已标记为删除。";}

是不是很简单?感觉就像是ORM在背后帮你做了个小魔术。

为什么选择软删除而非物理删除?

说实话,我个人在项目里几乎是能用软删除就用,除非业务逻辑明确要求数据必须物理销毁(比如用户注销,需要符合GDPR等隐私法规时)。为什么呢?

首先,数据安全和可恢复性。这是最直观的好处。手一抖,或者业务逻辑没考虑周全,物理删除的数据就真的没了,想找回来简直是噩梦。但软删除不一样,它只是个标记,你随时可以把它“恢复”回来。想想看,如果一个电商平台,用户不小心删了订单,或者运营人员误删了商品,能一键恢复是多么重要的事情。这就像给你的数据买了一份保险。

其次,审计和追踪。很多时候,我们需要知道数据曾经的存在状态,或者谁在什么时候“删除了”它。软删除保留了数据的完整历史,方便我们进行审计和追踪。这对于金融、医疗等合规性要求高的行业尤其重要。

如知AI笔记 如知AI笔记

如知笔记——支持markdown的在线笔记,支持ai智能写作、AI搜索,支持DeepseekR1满血大模型

如知AI笔记 27 查看详情 如知AI笔记

再来,用户体验。现在很多应用都有“回收站”或者“撤销”功能。软删除就是实现这些功能的基石。用户删了东西还能后悔,这种体验是物理删除无法提供的。

当然,软删除也不是万能药。它也有它的“副作用”。最明显的就是数据库体积膨胀。那些被标记为“删除”的数据依然占用存储空间。如果你的数据量非常大,并且有大量的软删除数据,可能会对数据库性能造成轻微影响,尤其是在没有正确索引的情况下。另外,查询的时候,如果需要排除软删除数据,可能会稍微增加查询复杂度(虽然ThinkPHP已经帮你处理好了)。但通常来说,这些问题在大多数应用场景下都是可控的,收益远大于成本。

如何查询包含或排除软删除的数据?

既然数据只是被标记了,那在查询的时候,系统怎么知道哪些是“正常”的,哪些是“已删除”的呢?ThinkPHP的软删除功能非常智能,它默认的行为就是自动排除那些 delete_time 不为 NULL 的记录。这意味着,你平时写查询,比如 User::where('status', 1)->select();,它返回的就已经是“未删除”的数据了。

但有时候,我们就是需要看到那些“已删除”的数据,比如在后台管理系统里,管理员可能需要查看所有数据,包括被删除的,或者只是想看看哪些数据被删了。ThinkPHP提供了几个非常方便的方法来控制查询行为:

查询所有(包括软删除数据):使用 withTrashed() 方法。

use appmodelUser;// 获取所有用户,包括那些已经被软删除的$allUsers = User::withTrashed()->select();// 这会生成类似 SELECT * FROM user WHERE delete_time IS NULL OR delete_time IS NOT NULL 的查询,或者更直接的 SELECT * FROM user

只查询软删除数据:使用 onlyTrashed() 方法。这在你想清理回收站或者查看删除历史时特别有用。

use appmodelUser;// 只获取那些已经被软删除的用户$trashedUsers = User::onlyTrashed()->select();// 这会生成类似 SELECT * FROM user WHERE delete_time IS NOT NULL 的查询

这些方法可以很灵活地和其他查询条件链式调用,满足你各种复杂的查询需求。比如,你想找某个时间段内被删除的用户:

use appmodelUser;$recentTrashedUsers = User::onlyTrashed()                            ->where('delete_time', '>', '2023-01-01 00:00:00')                            ->select();

软删除的数据如何恢复或彻底删除?

软删除的魅力之一就是它的可逆性。既然只是打了个标记,那自然也能把这个标记“擦掉”,让数据重新“活”过来。同时,如果你真的想彻底清理掉一些数据,ThinkPHP也提供了相应的强制删除方法。

恢复软删除的数据:使用模型实例的 restore() 方法。这会把 delete_time 字段重新设为 NULL

use appmodelUser;// 假设我们知道某个被软删除的用户的ID是10$trashedUser = User::onlyTrashed()->find(10);if ($trashedUser) {    $trashedUser->restore(); // 此时,ID为10的用户的delete_time字段会变回NULL    echo "用户ID:10 已成功恢复。";} else {    echo "未找到ID为10的已删除用户。";}

你也可以批量恢复,但这需要你先查询出这些被删除的数据集合:

// 恢复所有在某个时间点后被删除的用户User::onlyTrashed()->where('delete_time', '>', '2023-06-01 00:00:00')->restore();

彻底删除(物理删除)软删除的数据:有时候,你确实需要把数据从数据库里彻底抹掉,比如为了节省空间,或者出于合规性考虑。这时,你可以使用模型实例的 force() 方法,或者在 delete() 方法中传入 true 参数。

use appmodelUser;// 方式一:使用force()方法$userToDeletePermanently = User::onlyTrashed()->find(5); // 找到一个已被软删除的用户if ($userToDeletePermanently) {    $userToDeletePermanently->force(); // 此时,ID为5的记录会被从数据库中物理删除    echo "用户ID:5 已被物理删除。";}// 方式二:在delete()方法中传入true$anotherUserToDelete = User::find(6); // 找到一个正常的用户if ($anotherUserToDelete) {    $anotherUserToDelete->delete(true); // 此时,ID为6的记录会被直接物理删除,不会走软删除逻辑    echo "用户ID:6 已被物理删除。";}

值得注意的是,delete(true) 这种方式,无论你的模型是否开启了软删除,都会直接执行物理删除。而 force() 方法通常是在你已经查询出被软删除的数据后,再决定对其进行物理删除的操作。

在实际项目中,我通常会结合定时任务来清理那些“超期”的软删除数据,比如设置一个策略:被软删除超过90天的数据,就自动执行物理删除,这样既保证了短期内的数据可恢复性,又避免了数据库无限膨胀。这是一种比较平衡的做法。

以上就是ThinkPHP的软删除怎么实现?ThinkPHP如何标记删除数据?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/425161.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
让数据库变快的10个建议
上一篇 2025年11月7日 11:10:01
【VC++】字符串详解&窗口&第一个windows程序
下一篇 2025年11月7日 11:10:09

相关推荐

  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    500
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    500
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    500
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    300
  • 理解编程指令:当结果正确,但实现方式不符要求时

    本文探讨了在编程实践中,即使程序输出了正确的结果,但若其实现方式未能严格遵循既定指令,仍可能被视为“不正确”的问题。我们将通过具体示例,对比直接求和与累加求和两种实现策略,强调理解和遵守编程规范的重要性,以确保代码的健壮性、可维护性及符合项目要求。 在软件开发过程中,我们经常会遇到这样的情况:编写的…

    2026年5月10日
    000
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    300
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    400
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    700
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    300
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    1000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    500
  • Debian Copilot的社区活跃度如何

    debian copilot是codeberg社区维护的ai助手,旨在为debian用户提供服务。尽管搜索结果中没有直接提供关于debian copilot社区支持活跃度的具体数据,但我们可以通过debian社区的整体活跃度和特点来推断其活跃性。 Debian社区的一般情况: Debian拥有详尽的…

    2026年5月10日
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200
  • c++如何实现UDP通信_c++基于UDP的网络通信示例

    UDP通信基于套接字实现,适用于实时性要求高的场景。1. 流程包括创建套接字、绑定地址(接收方)、发送(sendto)与接收(recvfrom)数据、关闭套接字;2. 服务端监听指定端口,接收客户端消息并回传;3. 客户端发送消息至服务端并接收响应;4. 跨平台需处理Winsock初始化与库链接,编…

    2026年5月10日
    100
  • JavaScript函数中插入加载动画(Spinner)的正确方法

    本文旨在解决在JavaScript函数中插入加载动画(Spinner)时遇到的异步问题。通过引入async/await和Promise.all,确保在数据处理完成前后正确显示和隐藏加载动画,提升用户体验。我们将提供两种实现方案,并详细解释其原理和优势。 在Web开发中,当执行耗时操作时,显示加载动画…

    2026年5月10日
    700
  • JS如何实现迭代器?迭代器协议

    JavaScript中实现迭代器需遵循可迭代协议和迭代器协议,通过定义[Symbol.iterator]方法返回具备next()方法的迭代器对象,从而支持for…of和展开运算符;该机制统一了数据结构的遍历接口,实现惰性求值,适用于自定义对象、树、图及无限序列等复杂场景,提升代码通用性与…

    2026年5月10日
    300
  • 使用 Pydantic v2 实现条件性必填字段

    本文介绍了如何在 Pydantic v2 模型中实现条件性必填字段。通过自定义验证器,可以根据模型中其他字段的值来动态地控制某些字段是否为必填项,从而满足 API 交互中数据验证的复杂需求。本文提供了一个具体的示例,展示了如何确保模型中至少有一个字段被赋值。 在 Pydantic v2 中,虽然没有…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信