逻辑删除是通过 status 字段标记删除状态而非物理删除,所有查询必须加 status = 1 过滤,UPDATE 替代 DELETE,建议用 TINYINT(1) 或 ENUM 类型并加索引,需记录 deleted_at 并校验恢复操作。

PHP 中用 status 字段实现逻辑删除
直接删掉数据库记录是物理删除,风险高、不可逆;逻辑删除只是把 status 字段设为已删除状态(比如 0 或 'deleted'),后续查询时过滤掉这些记录。这是 PHP 项目里最常用、也最稳妥的软删除方案。
WHERE 条件里必须始终带上 status 过滤
一旦用了逻辑删除,所有读取数据的 SQL 都得默认排除已标记删除的行,否则用户会看到“不该看到的数据”。漏加 WHERE status = 1 是最常见、也最危险的疏忽。
- 列表页、详情页、关联查询,全部要检查是否加了
status = 1 - 使用 ORM(如 Laravel Eloquent)可全局作用域自动处理,但原生 PDO 或 MySQLi 必须手动写
- 别依赖前端传来的
id做唯一判断——攻击者可能传一个status = 0的 ID,后端没校验就返回数据
SELECT id, title FROM articles WHERE status = 1 AND id = ?
UPDATE 语句代替 DELETE 语句
原来写 DELETE FROM users WHERE id = ? 的地方,现在统一改成 UPDATE 更新状态字段。注意:不要用 INSERT ... ON DUPLICATE KEY UPDATE 或触发器模拟删除,那会增加复杂度和出错概率。
- 状态字段类型建议用
TINYINT(1)(值0/1)或ENUM('active','deleted'),避免用字符串如'disabled'增加拼写错误风险 - 给
status字段加索引,尤其当表数据量大、又频繁按状态筛选时 - 如果业务需要区分“已删除”“已归档”“已冻结”,就用多值枚举或整型状态码,但务必配好文档和常量定义
UPDATE orders SET status = 0, deleted_at = NOW() WHERE id = ? AND status = 1
真实场景中容易被忽略的细节
逻辑删除不是加个字段就完事,很多坑藏在边界里:
立即学习“PHP免费学习笔记(深入)”;
-
deleted_at字段建议一并记录,用于审计或后期清理,且能辅助判断“是否真删过” - 外键关联表(如评论、日志)要不要同步更新状态?通常不级联,而是查主表时 LEFT JOIN 并加主表状态条件
- 管理员后台“恢复删除”功能,本质就是
UPDATE ... SET status = 1,但必须校验原记录确实存在且当前是已删除态 - 搜索、导出、统计类接口最容易漏掉 status 过滤,特别是导出全量数据时,默认不能包含已删除项
状态字段本身不难,难的是整个系统所有数据流都对它保持一致认知——哪怕一个接口忘了加 status = 1,就等于逻辑删除形同虚设。











