本文目录导读:

在 PHP 中实现软删除有几种常见方式,我来详细介绍:
使用数据库字段实现(最基础)
创建表结构
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
email VARCHAR(100),
deleted_at TIMESTAMP NULL DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
基础 PHP 实现
<?php
class User {
private $db;
public function __construct($pdo) {
$this->db = $pdo;
}
// 软删除
public function softDelete($id) {
$sql = "UPDATE users SET deleted_at = NOW() WHERE id = ? AND deleted_at IS NULL";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$id]);
}
// 查询未删除的用户
public function getActiveUsers() {
$sql = "SELECT * FROM users WHERE deleted_at IS NULL";
$stmt = $this->db->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 查询包含已删除的用户
public function getAllUsers() {
$sql = "SELECT * FROM users";
$stmt = $this->db->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 恢复被删除的用户
public function restore($id) {
$sql = "UPDATE users SET deleted_at = NULL WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$id]);
}
// 永久删除
public function forceDelete($id) {
$sql = "DELETE FROM users WHERE id = ?";
$stmt = $this->db->prepare($sql);
return $stmt->execute([$id]);
}
}
?>
使用 Trait 复用(面向对象方式)
<?php
trait SoftDeletes {
public $deletedAt = 'deleted_at';
// 软删除
public function softDelete() {
if (!$this->exists()) {
return false;
}
$this->{$this->deletedAt} = date('Y-m-d H:i:s');
return $this->save();
}
// 恢复
public function restore() {
if ($this->{$this->deletedAt} === null) {
return false;
}
$this->{$this->deletedAt} = null;
return $this->save();
}
// 永久删除
public function forceDelete() {
return $this->delete(true);
}
// 检查是否已软删除
public function isTrashed() {
return !empty($this->{$this->deletedAt});
}
// 查询作用域
public static function withTrashed() {
// 返回包含已删除记录的查询
}
public static function onlyTrashed() {
// 只返回已删除的记录
}
}
class User {
use SoftDeletes;
protected $table = 'users';
// 重写 delete 方法支持软删除
public function delete($force = false) {
if (!$force && $this->{$this->deletedAt} === null) {
return $this->softDelete();
}
return parent::delete();
}
}
?>
使用 PDO 实现完整的软删除封装
<?php
abstract class Model {
protected $db;
protected $table;
protected $primaryKey = 'id';
protected $fillable = [];
protected $softDelete = true;
protected $deletedAt = 'deleted_at';
public function __construct($pdo) {
$this->db = $pdo;
}
// 删除操作
public function delete($id) {
if ($this->softDelete) {
return $this->softDeleteRecord($id);
}
return $this->forceDeleteRecord($id);
}
// 软删除实现
private function softDeleteRecord($id) {
$sql = "UPDATE {$this->table}
SET {$this->deletedAt} = :deleted_at
WHERE {$this->primaryKey} = :id
AND {$this->deletedAt} IS NULL";
$stmt = $this->db->prepare($sql);
$stmt->bindValue(':deleted_at', date('Y-m-d H:i:s'));
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
return $stmt->execute();
}
// 永久删除
private function forceDeleteRecord($id) {
$sql = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
return $stmt->execute();
}
// 查询所有记录的 where 条件构建
protected function buildWhereForActive($query) {
if ($this->softDelete) {
$query .= " AND {$this->deletedAt} IS NULL";
}
return $query;
}
// 查找单个记录
public function find($id) {
$sql = "SELECT * FROM {$this->table}
WHERE {$this->primaryKey} = ?";
if ($this->softDelete) {
$sql .= " AND {$this->deletedAt} IS NULL";
}
$stmt = $this->db->prepare($sql);
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
// 获取所有记录
public function all($includeTrashed = false) {
$sql = "SELECT * FROM {$this->table}";
if (!$includeTrashed && $this->softDelete) {
$sql .= " WHERE {$this->deletedAt} IS NULL";
}
$stmt = $this->db->query($sql);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
?>
使用现成框架(推荐)
Laravel 实现
<?php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model {
use SoftDeletes;
protected $table = 'users';
protected $dates = ['deleted_at'];
}
// 使用
$user = User::find(1);
$user->delete(); // 软删除
// 查询
$activeUsers = User::all(); // 只查未删除的
$allUsers = User::withTrashed()->get(); // 查所有
$onlyDeleted = User::onlyTrashed()->get(); // 只查已删除的
// 恢复
$user = User::withTrashed()->find(1);
$user->restore();
// 永久删除
$user->forceDelete();
?>
ThinkPHP 实现
<?php
namespace app\model;
use think\Model;
use think\model\concern\SoftDelete;
class User extends Model {
use SoftDelete;
protected $deleteTime = 'delete_time';
}
// 使用
$user = User::find(1);
$user->delete(); // 软删除
// 查询
$users = User::select(); // 只查未删除的
$all = User::withTrashed()->select(); // 查所有
$deleted = User::onlyTrashed()->select(); // 查已删除的
// 恢复
$user = User::onlyTrashed()->find(1);
$user->restore();
?>
MySQL 触发器方式(极端情况)
-- 创建触发器自动设置 deleted_at
DELIMITER //
CREATE TRIGGER before_delete_users
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
UPDATE users
SET deleted_at = NOW()
WHERE id = OLD.id AND deleted_at IS NULL;
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Soft delete only - cannot hard delete';
END//
DELIMITER ;
-- 但通常不推荐这种方案,因为灵活性差
最佳实践建议:
- 统一约定:字段
deleted_at使用NULL表示未删除,有值表示已删除时间 - 索引优化:为
deleted_at字段建立索引,提高查询效率 - 统一方法:封装统一的软删除、恢复、永久删除方法
- 框架优先:如果使用框架,优先使用框架的内置软删除功能
- 定时清理:定期清理长时间未恢复的软删除数据
- 注意查询:记得在业务查询中过滤
deleted_at IS NULL条件
选择哪种方式取决于项目的规模、使用的框架和个人偏好,对于中小型项目,直接使用数据库字段和简单封装就足够了。