PHP项目Laravel软删除功能如何用

wen PHP项目 4

本文目录导读:

PHP项目Laravel软删除功能如何用

  1. 📖 目录导读
  2. 为什么需要软删除?—— 数据安全与审计的基石
  3. 环境准备与数据库迁移 —— 开启软删除的第一步
  4. 模型配置 —— 让Eloquent“记住”删除状态
  5. 核心API实战 —— 销毁、恢复与永久清除
  6. 查询范围与排除 —— 如何筛选“活着”和“已死”的数据
  7. 高级技巧 —— 关联模型软删除、事件监听与清理策略
  8. 高频问答(FAQ)—— 解决你90%的困惑

📖 目录导读

  1. 为什么需要软删除?—— 数据安全与审计的基石
  2. 环境准备与数据库迁移 —— 开启软删除的第一步
  3. 模型配置 —— 让Eloquent“删除状态
  4. 核心API实战 —— 销毁、恢复与永久清除
  5. 查询范围与排除 —— 如何筛选“活着”和“已死”的数据
  6. 高级技巧 —— 关联模型软删除、事件监听与清理策略
  7. 高频问答(FAQ)—— 解决你90%的困惑

为什么需要软删除?—— 数据安全与审计的基石

在Web应用开发中,硬删除(物理删除)意味着数据从数据库彻底消失,这在许多业务场景(如订单回收站、用户注销恢复、内容审核)中是灾难性的,Laravel的软删除功能(SoftDeletes)本质上是一个逻辑标记:它并不真正删除记录,而是给数据表添加一个 deleted_at 时间戳字段,当该字段为 NULL 时,代表数据“活着”;当该字段被写入当前时间时,代表数据“已删除”。

这种机制带来了三大核心价值:

  • 可恢复性:误操作可以及时回滚。
  • 审计追踪:谁在何时删除了数据,有据可查。
  • 数据完整性:避免外键约束导致的历史数据断裂。

环境准备与数据库迁移 —— 开启软删除的第一步

我们需要在数据表中增加一个可空的 deleted_at 字段,最优雅的方式是在迁移文件中使用 softDeletes() 方法:

// database/migrations/xxxx_create_posts_table.php
public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        // ... 其他字段
        $table->timestamps();
        $table->softDeletes(); // 关键:添加 deleted_at 字段
    });
}
public function down()
{
    Schema::dropIfExists('posts');
}

如果你已有数据表,可以创建一个新迁移进行添加:

php artisan make:migration add_soft_deletes_to_posts_table --table=posts

然后在up()方法中使用 $table->softDeletes();down()中使用 $table->dropSoftDeletes();


模型配置 —— 让Eloquent“删除状态

这是最核心的一步,在对应的 Eloquent 模型中,引入 SoftDeletes trait:

// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
    use SoftDeletes; // 引入软删除特质
    protected $dates = ['deleted_at']; // 在 Laravel 7+ 中可选,默认已处理
    // 或者使用 $casts
    protected $casts = [
        'deleted_at' => 'datetime',
    ];
}

引入这个 trait 后,模型自动拥有了“查询作用域”行为:默认所有查询都会自动过滤掉 deleted_at 不为 NULL 的记录,这意味着你的普通 Post::all()Post::find($id) 永远只返回未删除的数据。


核心API实战 —— 销毁、恢复与永久清除

这是软删除的“三驾马车”操作。

1 软删除(销毁)

调用常规的 delete() 方法即可,但底层变为更新操作:

$post = Post::find(1);
$post->delete(); // 实际上执行了 UPDATE posts SET deleted_at = NOW() WHERE id = 1

注意:此操作不会真正移除记录。

2 恢复(Restore)

使用 restore() 方法将 deleted_at 置为 NULL

$post = Post::withTrashed()->find(1); // 必须先包含已删除的记录
$post->restore();

3 永久清除(Force Delete)

当数据确无保留价值时,使用 forceDelete() 执行物理删除(需先取到包含软删除的记录):

$post = Post::withTrashed()->find(1);
$post->forceDelete(); // 真正的 DELETE 语句

查询范围与排除 —— 如何筛选“活着”和“已死”的数据

Laravel 提供了三个极为实用的查询作用域:

方法 作用描述
Model::all() 常规查询,自动排除软删除数据(默认)
Model::withTrashed() 查询结果中包含软删除和未删除的所有记录
Model::onlyTrashed() 只获取已经被软删除的记录(回收站)

应用场景示例

  • 后台列表Post::withTrashed()->orderBy('deleted_at')->get(); 用于管理员的全局视图。
  • 回收站计数Post::onlyTrashed()->count(); 提供用户界面的红点提醒。
  • 唯一性校验允许重复,校验时需排除已删除记录:
    $exists = Post::withoutTrashed() // 显式声明只查未删除
      ->where('slug', $slug)
      ->exists();

高级技巧 —— 关联模型软删除、事件监听与清理策略

1 关联模型的级联软删除

假如帖子有多个评论,你希望删除帖子时评论也一并软删除,那需要在关联模型上也使用 SoftDeletes trait,并利用模型事件:

// Post.php
protected static function booted()
{
    static::deleting(function ($post) {
        if ($post->isForceDeleting()) {
            // 强制删除时,直接物理删除关联
            $post->comments()->forceDelete();
        } else {
            // 软删除时,级联软删除评论
            $post->comments()->delete();
        }
    });
    static::restoring(function ($post) {
        // 恢复帖子时,级联恢复评论
        $post->comments()->restore();
    });
}

2 全局作用域下的关联查询

一定要记住:$post->comments 会自动过滤掉已软删除的评论,除非使用 $post->comments()->withTrashed()->get()

3 清除过期回收站数据

配合任务调度(php artisan schedule:run),定期清理超过90天的软删除数据:

// 在 Console Kernel 中
$schedule->call(function () {
    Post::onlyTrashed()->where('deleted_at', '<', now()->subDays(90))->forceDelete();
})->daily();

高频问答(FAQ)—— 解决你90%的困惑

Q1:软删除会影响数据库索引和查询性能吗? A1: 会的,但可控。 由于大部分查询都带有 WHERE deleted_at IS NULL,建议为 deleted_at 字段添加索引,复合索引(如 (user_id, deleted_at))能显著提升特定用户的列表查询速度,注意,软删除会增加数据表体积,对于超大流量日志表建议使用分区表或归档。

Q2:开发中我误触发了软删除,马上恢复数据后,自增ID会变吗? A2: 完全不会。 软删除是更新操作,不占用新ID,自增ID仅在硬插入(INSERT)时发生,恢复后,记录数据完整无损,包括关联关系。

Q3:restore() 方法会恢复被软删除的关联模型吗?如果帖子被软删除了,怎么办? A3: 并不会自动恢复,除非你手动在模型事件中编写了 restored 事件侦听器(如上述第6章节所示),默认情况下,恢复帖子后,评论依然停留在软删除状态,你需要显式调用 $post->comments()->restore(),或者为 Post 模型注册 restored 事件。

Q4:使用 forceDelete() 会不会影响关联数据? A4: 会! 如果外键约束为 ON DELETE CASCADE,物理删除帖子会级联物理删除评论,如果外键有约束,而你强制删除了父级数据,子级数据可能会因外键约束报错,务必使用上述 isForceDeleting() 监听器来处理关联数据。

Q5:如何判断一条记录是否已被软删除? A5: 直接检查 $model->trashed() 方法,它返回布尔值:

if ($post->trashed()) {
    // 它在回收站
}

Q6:软删除字段可以改名吗?比如用 removed_at A6: 可以。 在模型中使用常量 DELETED_AT 覆盖默认名称:

const DELETED_AT = 'removed_at';

同时迁移文件中改为 $table->timestamp('removed_at')->nullable();,但官方默认 deleted_at 约定俗成,不建议随意修改。


Laravel软删除不是复杂的黑魔法,而是数据生命周期管理的标准答案,掌握 [withTrashedonlyTrashedrestoreforceDelete] 这四板斧,配合事件监听进行级联处理,你的应用数据安全性将提升一个量级,切勿滥用软删除(如日志流水、统计聚合表),动态权衡业务需求,才能让数据层既灵活又干净。

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