目錄
与第三方服务交互的技巧" >与第三方服务交互的技巧
使用自动或手动事务" >使用自动或手动事务
在数据库事务中调度队列" >在数据库事务中调度队列
首頁 php框架 Laravel 聊聊Laravel程式碼中怎麼正確地使用資料庫事務

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

Sep 30, 2022 pm 05:45 PM
php 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([
        &#39;email&#39; => $request->email,
    ]);

    $user->roles()->attach(Role::where(&#39;name&#39;, &#39;general&#39;)->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([
        &#39;email&#39; => $request->email,
    ]);

    $user->roles()->attach(Role::where(&#39;name&#39;, &#39;general&#39;)->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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP的目的:構建動態網站 PHP的目的:構建動態網站 Apr 15, 2025 am 12:18 AM

PHP用於構建動態網站,其核心功能包括:1.生成動態內容,通過與數據庫對接實時生成網頁;2.處理用戶交互和表單提交,驗證輸入並響應操作;3.管理會話和用戶認證,提供個性化體驗;4.優化性能和遵循最佳實踐,提升網站效率和安全性。

PHP和Python:解釋了不同的範例 PHP和Python:解釋了不同的範例 Apr 18, 2025 am 12:26 AM

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP:處理數據庫和服務器端邏輯 PHP:處理數據庫和服務器端邏輯 Apr 15, 2025 am 12:15 AM

PHP在數據庫操作和服務器端邏輯處理中使用MySQLi和PDO擴展進行數據庫交互,並通過會話管理等功能處理服務器端邏輯。 1)使用MySQLi或PDO連接數據庫,執行SQL查詢。 2)通過會話管理等功能處理HTTP請求和用戶狀態。 3)使用事務確保數據庫操作的原子性。 4)防止SQL注入,使用異常處理和關閉連接來調試。 5)通過索引和緩存優化性能,編寫可讀性高的代碼並進行錯誤處理。

為什麼要使用PHP?解釋的優點和好處 為什麼要使用PHP?解釋的優點和好處 Apr 16, 2025 am 12:16 AM

PHP的核心優勢包括易於學習、強大的web開發支持、豐富的庫和框架、高性能和可擴展性、跨平台兼容性以及成本效益高。 1)易於學習和使用,適合初學者;2)與web服務器集成好,支持多種數據庫;3)擁有如Laravel等強大框架;4)通過優化可實現高性能;5)支持多種操作系統;6)開源,降低開發成本。

在PHP和Python之間進行選擇:指南 在PHP和Python之間進行選擇:指南 Apr 18, 2025 am 12:24 AM

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

laravel入門實例 laravel入門實例 Apr 18, 2025 pm 12:45 PM

Laravel 是一款 PHP 框架,用於輕鬆構建 Web 應用程序。它提供一系列強大的功能,包括:安裝: 使用 Composer 全局安裝 Laravel CLI,並在項目目錄中創建應用程序。路由: 在 routes/web.php 中定義 URL 和處理函數之間的關係。視圖: 在 resources/views 中創建視圖以呈現應用程序的界面。數據庫集成: 提供與 MySQL 等數據庫的開箱即用集成,並使用遷移來創建和修改表。模型和控制器: 模型表示數據庫實體,控制器處理 HTTP 請求。

PHP和Python:深入了解他們的歷史 PHP和Python:深入了解他們的歷史 Apr 18, 2025 am 12:25 AM

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

PHP:服務器端腳本語言的簡介 PHP:服務器端腳本語言的簡介 Apr 16, 2025 am 12:18 AM

PHP是一種服務器端腳本語言,用於動態網頁開發和服務器端應用程序。 1.PHP是一種解釋型語言,無需編譯,適合快速開發。 2.PHP代碼嵌入HTML中,易於網頁開發。 3.PHP處理服務器端邏輯,生成HTML輸出,支持用戶交互和數據處理。 4.PHP可與數據庫交互,處理表單提交,執行服務器端任務。

See all articles