[扩展包] Laravel-softdeletes 让数据表支持唯一索引,用于替代内置的软删除功能

前言

Laravel内置的软删除功能是一个非常方便的功能,但是这个功能有个很大的缺点:使用了软删功能会导致不能给字段增加唯一索引(unique)。因为一旦给数据表增加了唯一索引,那么被软删的数据很容易就与未删除的正常数据产生冲突,在绝大多数业务中这种情况都是不允许发生的!比如用户表的手机号,邮箱等。

所以如果你的数据表中的字段有需要保持唯一性的需求,一旦使用Laravel内置的软软删除功能,那就需要靠程序逻辑去保证字段的唯一性,这会给日常开发带来很多不必要的麻烦,增加不必要的开销!比如我司的一部分业务使用了软删除功能,所以字段不能增加唯一索引,就导致需要在程序逻辑中对数据加锁处理,大大降低了程序的的性能。

Laravel内置的软删除功能的另外一个不足就是被软删除的数据一般都是很少用到的,所以跟正常数据放在同一张数据表对性能也会有所影响。

Laravel Softdeletes

所以基于以上原因,我写了Laravel softdeletes,用于替代Laravel内置的软删除(softDelets)功能,可以把软删数据存储到独立的数据表中,从而不影响原始表字段增加唯一索引,且可以起到提升性能的作用,用法与内置功能几乎100%一致。

github.com/jqhph/laravel-softdelet... (如果喜欢这个项目不妨点个 star,谢谢支持~)

环境

  • PHP >= 7
  • laravel >= 5.5

安装

composer require dcat/laravel-softdeletes

使用

首先需要创建两张字段一模一样的数据表,并且两张表都需要添加deleted_at字段

// 文章表
Schema::create('posts', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('title')->nullable();
    $table->string('body')->nullable();

    // 两张表都需要删除时间字段
    $table->timestamp('deleted_at')->nullable();

    $table->timestamps();
});

// 文章软删除表
Schema::create('posts_trash', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('title')->nullable();
    $table->string('body')->nullable();

    // 两张表都需要删除时间字段
    $table->timestamp('deleted_at')->nullable();

    $table->timestamps();
});

模型定义如下,默认的软删除表表名为:{原始表}_trash,如上面的posts表的默认软删除表表名是posts_trash

<?php

namespace App\Models;

use Dcat\Laravel\Database\SoftDeletes;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use SoftDeletes;

    /**
     * 自定义软删除表表名,默认为 {$table}_trash 
     * 
     * @var string 
     */
    protected $trashedTable = 'posts_trash';

    /**
     * 自定义软删除表表名,如有需要可以重写此方法
     * 
     * @return mixed
     */
    public function getTrashedTable()
    {
        return $this->trashedTable;
    }
}

除了withTrashed只能用于查询数据之外,其他方法的使用与Laravel内置的软删除功能完全一致,下面是简单的用法示例

需要注意withTrashed只能用于查询数据,不能用于updatedelete!!!

查询正常表数据

$posts = Post::where(...)->get();

仅查询软删除表数据 (onlyTrashed)

$trashedPosts = Post::onlyTrashed()->where(...)->get();

同时查询正常和软删数据 (withTrashed),需要注意withTrashed只能用于查询数据,不能用于updatedelete!!!

Post::withTrashed()
    ->where(...)
    ->offset(5)
    ->limit(5)
    ->get();

// 可以使用子查询以及whereHas等
Post::withTrashed()
    ->whereHas('...', function ($q) {
        $q->where(...);
    })
    ->offset(5)
    ->limit(5)
    ->get();

// 分页
Post::withTrashed()
    ->whereHas('...', function ($q) {
     $q->where(...);
    })
    ->paginate(10);

软删除/硬删除/还原

$post = Post::first();

// 软删除
$post->delete();

// 还原
$post->restore();

// 硬删
$post->forceDelete();

// 批量软删
Post::where(...)->delete();

// 批量硬删
Post::onlyTrashed()->where(...)->delete();
本作品采用《CC 协议》,转载必须注明作者和本文链接
Jiangqh
本帖由系统于 4个月前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 4
JaguarJack

暴力解决,需要唯一的,不用软删除 :relaxed:

9个月前 评论
Jiangqh (楼主) 9个月前

有用,学习了。

7个月前 评论

发现一个问题,先正常删除数据,删除的数据会写入到trash表中,主表数据确实是没有了;然后使用withTrashed查找已删除的数据,并使用restore将该数据还原,还原之后再次执行删除该数据操作时会提示Integrity constraint violation: 1062 Duplicate entry,主键重复,这个应该是个bug吧

7个月前 评论
Jiangqh (楼主) 7个月前
Complicated

老铁,能不能留一个地方可以修改deleted_at的字段类型,不喜欢用时间戳,就用 0 1表示就好了

4个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!