[扩展包] 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
本帖由系统于 1周前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 4
JaguarJack

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

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

有用,学习了。

3个月前 评论

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

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

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

1周前 评论

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