PHP 软删除实现方式

wen PHP项目 2

本文目录导读:

PHP 软删除实现方式

  1. 使用数据库字段实现(最基础)
  2. 使用 Trait 复用(面向对象方式)
  3. 使用 PDO 实现完整的软删除封装
  4. 使用现成框架(推荐)
  5. MySQL 触发器方式(极端情况)
  6. 最佳实践建议:

在 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 ;
-- 但通常不推荐这种方案,因为灵活性差

最佳实践建议:

  1. 统一约定:字段 deleted_at 使用 NULL 表示未删除,有值表示已删除时间
  2. 索引优化:为 deleted_at 字段建立索引,提高查询效率
  3. 统一方法:封装统一的软删除、恢复、永久删除方法
  4. 框架优先:如果使用框架,优先使用框架的内置软删除功能
  5. 定时清理:定期清理长时间未恢复的软删除数据
  6. 注意查询:记得在业务查询中过滤 deleted_at IS NULL 条件

选择哪种方式取决于项目的规模、使用的框架和个人偏好,对于中小型项目,直接使用数据库字段和简单封装就足够了。

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