搜尋
首頁php框架Laravel聊聊Laravel程式碼中怎麼正確地使用資料庫事務

聊聊Laravel程式碼中怎麼正確地使用資料庫事務

在 web 開發中,資料的完整性和準確性非常重要。因此,必須確保我們編寫的程式碼能夠以安全的方式儲存、更新和刪除資料庫中的資料。

在本文中,我們將看看什麼是資料庫事務,為什麼它們很重要,以及如何在 Laravel 開始使用它們。我們還將研究一個常見的“問題”,涉及隊列和資料庫事務。

什麼是資料庫事務?

在我們開始研究Laravel 的資料庫事務之前,讓我們先看看它們是什麼以及它們如何有益。

對於什麼是資料庫事務,有許多聽起來複雜的技術解釋。但是,對於大多數 web 開發人員來說,我們只需要知道事務是完成資料庫中整個工作單元的方式。

為了理解這實際上意味著什麼,讓我們來看一個基本的例子,它將給出一點上下文。

假設我們有一個允許用戶註冊的應用程式。每當用戶註冊時,我們都希望為他們建立一個新帳戶,然後為他們分配一個預設角色「 general」。

我們的程式碼可能是這樣的:

$user = User::create([
    'email' => $request->email,
]);

$user->roles()->attach(Role::where('name', 'general')->first());

乍一看,這段程式碼似乎完全沒問題。但是,當我們仔細觀察的時候,我們可以發現實際上有一些事情可能會出錯。我們可以創建用戶,但是不能為他們分配角色。這可能是由許多不同的原因造成的,例如分配角色的程式碼中的錯誤,甚至是阻止我們到達資料庫的硬體問題。

由於這種情況的發生,這將意味著系統中將有一個沒有角色的使用者。正如您可以想像的那樣,這可能會在您的應用程式中的其他地方引起異常和 bug,因為您總是假設用戶有一個角色(這是正確的)。

因此,為了解決這個問題,我們可以使用資料庫事務。透過使用事務,它可以確保在執行程式碼時,如果出現任何錯誤,事務內部對資料庫的任何變更都會回滾。例如,如果使用者被插入到資料庫中,但是由於任何原因分配角色的查詢失敗,那麼交易將被回滾,用戶行將被刪除。透過這樣做,它意味著我們不能創建沒有分配角色的使用者。

換句話說,它「要麼全有,要麼全沒有」。

在Laravel 中使用資料庫事務

現在我們對事務是什麼以及它們實現了什麼有了一個簡單的概念,讓我們來看看如何在Laravel 中使用它們。

在 Laravel 中,由於我們可以在 DB 門面上存取 transaction() 方法,因此開始使用事務實際上是很容易的事。繼續使用先前的範例程式碼,讓我們看看在建立使用者並為其指派角色時如何使用交易。

use Illuminate\Support\Facades\DB;

DB::transaction(function () use ($user, $request): void {
    $user = User::create([
        'email' => $request->email,
    ]);

    $user->roles()->attach(Role::where('name', 'general')->first());
});

現在我們的程式碼被包裹在一個資料庫事務中,如果在其中的任意一點拋出異常,對資料庫的任何更改都將返回到事務開始之前的狀態。

在 Laravel 中手動使用資料庫交易

有時,您可能希望對交易進行更精細的控制。例如,假設您正在與第三方服務集成,例如 Mailchinp 或 Xero。我們會說,每當您建立新使用者時,您還需要向他們的 API 發出 HTTP 請求,以將他們也建立為該系統中的使用者。

我們可能想要更新我們的程式碼,以便如果我們無法在我們自己的系統 ** 且 ** 在第三方系統中建立用戶,則兩個系統都不建立用戶。如果您正在與第三方系統交互,那麼您可能有一個可用於發出請求的類別。或者,可能有一個您可以使用的包。有時,當某些請求無法完成時,發出請求的類別可能會拋出異常。然而,其中一些類別可能會消除錯誤,而只是從您呼叫的方法中傳回 false,並將錯誤放置在類別的欄位中。

因此,我們假設我們有以下呼叫API 的基本範例類別:

class ThirdPartyService
{
    private $errors;

    public function createUser($userData)
    {
        $request = $this->makeRequest($userData);

        if ($request->successful()) {
            return $request->body();
        }

        $errors = $request->errors();

        return false;
    }

    public function getErrors()
    {
        return $this->errors;
    }
}

當然,上面的請求類別程式碼是不完整的,我下面的程式碼範例也不是很清楚,但它應該能讓您大致了解我要表達的觀點。所以讓我們使用這個請求類別並將其添加到我們之前的程式碼範例中:

use Illuminate\Support\Facades\DB;
use App\Services\ThirdPartyService;

DB::beginTransaction();

$thirdPartyService = new ThirdPartyService();

$userData = [
    'email' => $request->email,
];

$user = User::create($userData);

$user->roles()->attach(Role::where('name', 'general')->first());

if ($thirdPartyService->createUser($userData)) {
    DB::commit();

    return;
}

DB::rollBack();

report($thirdPartyService->getErrors());

查看上面的程式碼,我們可以看到我們啟動了一個事務,創建了用戶並為他們分配了一個角色,然後我們呼叫了第三方服務。如果在外部服務中成功創建了用戶,知道所有內容都已正確創建,我們就可以安全地提交資料庫變更。但是,如果沒有在外部服務中建立用戶,則回滾資料庫中的變更(刪除用戶及其角色分配),然後報告錯誤。

与第三方服务交互的技巧

作为一个额外的技巧,我通常建议将任何影响第三方系统、文件存储或缓存的代码放在数据库调用之后

为了更深入地理解这一点,让我们以上面的代码示例为例。请注意,在向第三方服务发出请求之前,我们是如何首先对数据库进行所有更改的。这意味着,如果从第三方请求返回任何错误,将回滚我们自己数据库中的用户和角色分配。

然而, 如果我们反过来做,我们在修改数据库之前发出请求,那就不是这种情况了。出于任何原因,如果我们在数据库中创建用户时发生任何错误,我们会在第三方系统中创建一个新用户,但是在我们系统中却没有创建。如你所想, 这可能会导致更多问题。通过编写一个清理方法将用户从第三方系统中删除,可以降低这个问题的严重性。 但是,正如您可以想象的那样, 这可能会导致更多的问题,并导致编写、维护和测试更多的代码。

所以,我总是建议把数据库调用放在API调用之前。但并不总是这样,有时可能需要将第三方请求返回的值保存到数据库中。如果是这种情况,就需要API调用放到数据库调用之前了,只要您确保有一些代码可以处理任何失败,这是完全可以的。

使用自动或手动事务

同样值得注意的是,因为我们最初的示例使用DB:transaction()方法,在抛出异常时回滚事务,所以我们也可以使用这种方法向我们的第三方服务发出请求。相反,我们可以这样更新类:

use Illuminate\Support\Facades\DB;
use App\Services\ThirdPartyService;

DB::transaction(function () use ($user, $request): void {
    $user = User::create([
        'email' => $request->email,
    ]);

    $user->roles()->attach(Role::where('name', 'general')->first());

    if (! $thirdPartyService->createUser($userData)) {
        throw new \Exception('User could not be created');
    }
});

这绝对是一个可行的解决方案,并将按照预期成功回滚事务。事实上,就我个人的偏好而言,我实际上更喜欢这种方式,而不是手动使用事务。我认为它看起来更容易阅读和理解。

然而,与手动提交或回滚事务时使用 ‘if’ 语句相比,异常处理在时间和性能方面可能会比较昂贵。

因此,举个例子,如果这段代码用于导入包含10,000个用户数据的 CSV 文件,您可能会发现抛出异常会大大减慢导入速度。

但是,如果它只是在一个用户可以注册的简单web请求中使用,那么抛出异常可能没有问题。当然,这取决于应用程序的大小,性能是关键因素;所以你需要根据具体情况来决定。

在数据库事务中调度队列

每当您在事务中处理队列时,您都需要注意一个“陷阱”。

为了提供一些上下文,让我们继续使用之前的代码示例。我们可以想象,在我们创建了我们的用户之后,我们想要运行一个任务来提醒管理员通知他们新注册并向新用户发送欢迎电子邮件。我们将通过分派一个名为 AlertNewUser 的队列任务来做到这一点,如下所示:

use Illuminate\Support\Facades\DB;
use App\Jobs\AlertNewUser;
use App\Services\ThirdPartyService;

DB::transaction(function () use ($user, $request): void {
    $user = User::create([
        'email' => $request->email,
    ]);

    $user->roles()->attach(Role::where('name', 'general')->first());

    AlertNewUser::dispatch($user);
});

当您开始一个事务并对其中的任何数据进行更改时,这些更改仅对正在运行事务的请求/进程可用。对于任何其他访问您更改的数据的请求或进程,必须先提交事务。因此,这意味着如果我们从事务内部分派任何排队的队列、事件监听器、邮件,通知或广播事件。由于竞争条件,我们的数据更改可能在事务内部不可用。

如果队列在事务提交之前开始处理排队的代码,就会发生这种情况。因此,这可能导致您的排队代码可能试图访问不存在的数据,并可能导致错误。在我们的例子中,如果在事务提交之前运行队列AlertNewUser作业,那么我们的作业将尝试访问一个尚未实际存储在数据库中的用户。如您所料,这将导致作业失败。

为了防止这种竞争条件的发生,我们可以对我们的代码和/或我们的配置进行一些更改,以确保仅在事务成功提交后才调度队列。

我们可以更新 config/queue.php 并添加 after commit 字段。让我们想象一下,我们正在使用 redis 队列驱动程序,所以我们可以这样更新配置:

<?php

return [

    // ...

    &#39;connections&#39; => [

        // ...

        &#39;redis&#39; => [
            &#39;driver&#39; => &#39;redis&#39;,
            // ...
            &#39;after_commit&#39; => true,
        ],

        // ...

    ],

    // ...
];

通过进行此更改,如果我们尝试在事务内调度队列,则队列将在实际调度队列之前等待事务提交。 方便的是,如果事务回滚,它也会阻止队列被调度。

然而,可能有一个原因,您不希望在配置中全局设置此选项。 如果是这种情况,Laravel 仍然提供了一些很好的助手方法,我们可以根据具体情况使用它们。
如果我们想更新事务中的代码,只在任务提交后才分派任务,可以使用afterCommit()方法,如下所示:

use Illuminate\Support\Facades\DB;
use App\Jobs\AlertNewUser;
use App\Services\ThirdPartyService;

DB::transaction(function () use ($user, $request): void {
    $user = User::create([
        'email' => $request->email,
    ]);

    $user->roles()->attach(Role::where('name', 'general')->first());

    AlertNewUser::dispatch($user)->afterCommit();
});

Laravel 还提供了另一个我们可以使用的方便的beforeCommit()方法。 如果我们在队列配置中设置了全局after_commit => true,但不关心等待事务被提交,就可以使用这个。 要做到这一点,我们可以简单地像这样更新我们的代码:

use Illuminate\Support\Facades\DB;
use App\Jobs\AlertNewUser;
use App\Services\ThirdPartyService;

DB::transaction(function () use ($user, $request): void {
    $user = User::create([
        'email' => $request->email,
    ]);

    $user->roles()->attach(Role::where('name', 'general')->first());

    AlertNewUser::dispatch($user)->beforeCommit();
});

总结

希望本文能让您大致了解什么是数据库事务以及如何在 Laravel 中使用它们。 它还向您展示了如何在从内部事务调度队列时避免“陷阱”。

原文地址:https://dev.to/ashallendesign/using-database-transactions-to-write-safer-laravel-code-13ek

译文地址:https://learnku.com/laravel/t/61575

【相关推荐:laravel视频教程

以上是聊聊Laravel程式碼中怎麼正確地使用資料庫事務的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:learnku。如有侵權,請聯絡admin@php.cn刪除
Laravel(PHP)與Python:不同的用例和應用Laravel(PHP)與Python:不同的用例和應用Apr 18, 2025 am 12:16 AM

選擇Laravel或Python取決於項目需求:1)若需快速開發Web應用並使用ORM和認證系統,選Laravel;2)若涉及數據分析、機器學習或科學計算,選Python。

Laravel和Python:找到合適的工具Laravel和Python:找到合適的工具Apr 18, 2025 am 12:14 AM

Laravel适合快速构建Web应用,Python适用于需要灵活性和多功能性的项目。1)Laravel提供丰富功能如ORM和路由,适合PHP生态系统。2)Python以简洁语法和强大库生态系统著称,适用于Web开发和数据科学等领域。

Laravel和PHP:創建動態網站Laravel和PHP:創建動態網站Apr 18, 2025 am 12:12 AM

使用Laravel和PHP可以高效且有趣地創建動態網站。 1)Laravel遵循MVC架構,Blade模板引擎簡化HTML編寫。 2)路由系統和請求處理機制使URL定義和用戶輸入處理變得簡單。 3)EloquentORM簡化數據庫操作。 4)通過博客系統示例展示了數據庫遷移、CRUD操作和Blade模板的使用。 5)Laravel提供了強大的用戶認證和授權功能。 6)調試技巧包括使用日誌系統和Artisan工具。 7)性能優化建議包括惰性加載和緩存。

拉維爾(Laravel)和完整的堆棧:前後一起拉維爾(Laravel)和完整的堆棧:前後一起Apr 18, 2025 am 12:07 AM

Laravel通過Blade模板引擎、EloquentORM、Artisan工具和LaravelMix實現全棧開發:1.Blade簡化前端開發;2.Eloquent簡化數據庫操作;3.Artisan提高開發效率;4.LaravelMix管理前端資源。

Laravel:現代網絡開發的框架Laravel:現代網絡開發的框架Apr 18, 2025 am 12:05 AM

Laravel是一個基於PHP的現代化框架,遵循MVC架構模式,提供了豐富的工具和功能,簡化了Web開發過程。 1)它包含EloquentORM用於數據庫交互,2)Artisan命令行接口用於快速生成代碼,3)Blade模板引擎用於高效的視圖開發,4)強大的路由系統用於定義URL結構,5)認證系統用於用戶管理,6)事件監聽和廣播用於實時功能,7)緩存和隊列系統用於性能優化,使得構建和維護現代Web應用變得更加容易和高效。

Laravel(PHP)與Python:權衡優點和缺點Laravel(PHP)與Python:權衡優點和缺點Apr 17, 2025 am 12:18 AM

Laravel适合快速构建Web应用,而Python适用于更广泛的应用场景。1.Laravel提供EloquentORM、Blade模板引擎和Artisan工具,简化Web开发。2.Python以动态类型、丰富的标准库和第三方生态系统著称,适用于Web开发、数据科学等领域。

Laravel vs. Python:比較框架和圖書館Laravel vs. Python:比較框架和圖書館Apr 17, 2025 am 12:16 AM

Laravel和Python各有優勢:Laravel適合快速構建功能豐富的Web應用,Python在數據科學和通用編程領域表現出色。 1.Laravel提供EloquentORM和Blade模板引擎,適合構建現代Web應用。 2.Python擁有豐富的標準庫和第三方庫,Django和Flask框架滿足不同開發需求。

Laravel的目的:構建強大而優雅的Web應用程序Laravel的目的:構建強大而優雅的Web應用程序Apr 17, 2025 am 12:13 AM

Laravel值得選擇,因為它能使代碼結構清晰,開發過程更具藝術性。 1)Laravel基於PHP,遵循MVC架構,簡化Web開發。 2)其核心功能如EloquentORM、Artisan工具和Blade模板增強了開發的優雅與健壯性。 3)通過路由、控制器、模型和視圖,開發者能高效構建應用。 4)隊列和事件監聽等高級功能進一步提升應用性能。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。