Eloquent ORM - 软删除

一般删除的话资料就永远消失了,有时候我们为了避免某些重要资料不小心删除後再也无法救回的情况,会让资料变成软删除模式。

软删除指的是在进行删除後不是将资料清除,而是给资料打上"已删除"的标签,这样在之後查询资料时自动避开这些被打上已删除标签的资料。

Laravel 也有提供简便设定资料软删除的方法,接着就来实作。

变更资料结构

首先要帮资料表加上软删除标签的栏位,建立一个更新 users 资料表的 migration。

sail artisan make:migration update_user_add_soft_delete --table users

Migration 的 up / down 内容

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->softDeletes(); //加入 deleted_at 栏位
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    Schema::table('users', function (Blueprint $table) {
        $table->dropSoftDeletes();
    });
}

$table->softDeletes() 会新增一个日期格式的 deleted_at 栏位,作为 Model 进行删除跟查询时的依据。

别忘了建好後要跑 migration

sail artisan migrate

再来要在 Model 中加上对应软删除的特性

/app/Models/User.php

@@ -7,10 +7,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
+use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
+    use SoftDeletes;

    /**
     * The attributes that are mass assignable.

这样就设定好模型了

路由

接着新增一条删除 User 用的路由

/routes/web.php

@@ -62,3 +62,7 @@ Route::post('/confirm-password', [ConfirmablePasswordController::class, 'store']
 Route::post('/logout', [AuthenticatedSessionController::class, 'destroy'])
                 ->middleware('auth')
                 ->name('logout');
+
+Route::post('/delete-account', [RegisteredUserController::class, 'destroy'])
+                ->middleware('auth')
+                ->name('account.delete');

路由指向 RegisteredUserController 的 destroy 方法,这东西还不存在所以也要新增。

/app/Http/Controllers/Auth/RegisteredUserController.php

@@ -52,4 +52,9 @@ class RegisteredUserController extends Controller
+    public function destroy(Request $request)
+    {  
+    } 

使用 Auth 取得登入用户资讯

接下来要怎麽找出该删哪个 user 呢? 传送 id 然後用 find 找出来删掉是一个方法,不过 Laravel 提供了更方便的方法。

因为我们给路由加上了 middleware('auth') ,代表这是个需要经过身分验证才能请求的路由,而经过这类路由的请求都可以用 Laravel 的 Auth 取得发送请求的 User 资讯。

一般来说也只有用户本人可以删除自己,所以直接找出发送请求的用户并将他的资料删除。

/app/Http/Controllers/Auth/RegisteredUserController.php

@@ -55,6 +55,8 @@ class RegisteredUserController extends Controller
 
 
     public function destroy(Request $request)
-    { 
+    {
+        $user = Auth::user(); // 取得用户资料
+        $user->delete();  // 删除用户
     }

删除 user 之後就是跟登出一样的步骤,消除 session 然後导向首页。

@@ -57,6 +57,14 @@ class RegisteredUserController extends Controller
     public function destroy(Request $request)
     {
         $user = Auth::user();
         $user->delete();  
+
+        Auth::guard('web')->logout();
+
+        $request->session()->invalidate();
+        
+        $request->session()->regenerateToken();
+
+        return redirect('/');
     }

接着来建立前端的请求。

删除帐号按钮

把删除帐号的选项做在右上角的下拉选单中

/resources/js/Layouts/Authenticated.js

@@ -62,6 +62,12 @@ export default function Authenticated({ auth, header, children }) {
                       as='button'>
                       Log Out
                     </Dropdown.Link>
+                    <Dropdown.Link
+                      href={route("account.delete")}
+                      method='post'
+                      as='button'>
+                      Delete Account
+                    </Dropdown.Link>
                   </Dropdown.Content>
                 </Dropdown>
               </div>

直接向刚刚建立的 account.delete 路由发送请求,不用带任何参数。

画面更新好後就可以勇敢点下去啦,步骤都正确的话就会被导向 Laravel 的首页,然後没办法再用同一个帐号登入。

可以到资料库中看看,刚刚被删除的帐号,他的 deleted_at 会被押上时间,这样一般进行搜寻的时候就会被忽略掉。

进阶使用

如果要把被软删除的资料复原的话,用 restore()

$user->restore();

可以在搜寻资料的时候指定要包含被删除的资料

User::withTrashed()->restore(); // 复原所有被软删除的资料

或是只找出被删除的资料

$deletedUsers = User::onlyTrashed()->get();

不想软删除而是完全删除的话也有方法

$user->forceDelete(); // 硬删除一笔资料
User::onlyTrashed()->forceDelete(); // 硬删除所有被软删除的资料

自订 deleted_at 栏位名称

不想用 deleted_at 作为栏位名称的话,可以在 Model 中宣告自订的 DELETED_AT 名称。

@@ -13,6 +13,8 @@ class User extends Authenticatable
 {
     use HasApiTokens, HasFactory, Notifiable;
     use SoftDeletes;
+    
+    const DELETED_AT = 'deletedAt';

migration 里也要改名称

$table->softDeletes('deletedAt');

<<:  Day-19 ADT与链结串列(linked list)

>>:  JS Library 学习笔记:首先当然来试试 jQuery (四)

Day7 跟着官方文件学习Laravel-开始学习Command用法

注册的方式我想使用laravel的command来实作,原本想用form表单来实现,不过这样感觉主题...

【第二十二天 - DFS 介绍】

Q1. DFS 是什麽 Depth-First Search (DFS) 是一种走访 Graph 的...

Day27 小乌龟动工的基本指令集

今天来看看怎麽让一个小乌龟动起来! 从最基本的 Turtle 开始,相关程序码在 /rom/prog...

[职场]掌握薪水谈判的秘诀,取得自己应有的报酬 & 完赛感言

如果你是一个有野心且不安於现状的人,薪水谈判是你一定要去做的事情。 在阅读这篇文章前,你可以先思考...

Day 23-Unit Test 应用於 DateTime-2 (情境及应用-3)

Unit Test 应用於 DateTime-前言-2 今天文章的内容是参考於 C# - how t...