這篇文章主要介紹“Laravel代碼中如何正確使用數(shù)據(jù)庫事務”,在日常操作中,相信很多人在Laravel代碼中如何正確使用數(shù)據(jù)庫事務問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Laravel代碼中如何正確使用數(shù)據(jù)庫事務”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
創(chuàng)新互聯(lián)堅持“要么做到,要么別承諾”的工作理念,服務領域包括:成都網(wǎng)站制作、做網(wǎng)站、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣等服務,滿足客戶于互聯(lián)網(wǎng)時代的邵武網(wǎng)站設計、移動媒體設計的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡建設合作伙伴!
在我們開始研究 Laravel 的數(shù)據(jù)庫事務之前,讓我們先看看它們是什么以及它們?nèi)绾斡幸妗?/p>
對于什么是數(shù)據(jù)庫事務,有許多聽起來復雜的技術解釋。但是,對于大多數(shù) web 開發(fā)人員來說,我們只需要知道事務是完成數(shù)據(jù)庫中整個工作單元的方式。
為了理解這實際上意味著什么,讓我們來看一個基本的例子,它將給出一點上下文。
假設我們有一個允許用戶注冊的應用程序。每當用戶注冊時,我們都希望為他們創(chuàng)建一個新帳戶,然后為他們分配一個默認角色“ general”。
我們的代碼可能是這樣的:
$user = User::create([
'email' => $request->email,
]);
$user->roles()->attach(Role::where('name', 'general')->first());
乍一看,這段代碼似乎完全沒問題。但是,當我們仔細觀察的時候,我們可以發(fā)現(xiàn)實際上有一些事情可能會出錯。我們可以創(chuàng)建用戶,但是不能為他們分配角色。這可能是由許多不同的原因造成的,比如分配角色的代碼中的錯誤,或者甚至是阻止我們到達數(shù)據(jù)庫的硬件問題。
由于這種情況的發(fā)生,這將意味著系統(tǒng)中將有一個沒有角色的用戶。正如您可以想象的那樣,這可能會在您的應用程序中的其他地方引起異常和 bug,因為您總是假設用戶有一個角色(這是正確的)。
因此,為了解決這個問題,我們可以使用數(shù)據(jù)庫事務。通過使用事務,它可以確保在執(zhí)行代碼時,如果出現(xiàn)任何錯誤,事務內(nèi)部對數(shù)據(jù)庫的任何更改都將回滾。例如,如果用戶被插入到數(shù)據(jù)庫中,但是由于任何原因分配角色的查詢失敗,那么事務將被回滾,用戶行將被刪除。通過這樣做,它意味著我們不能創(chuàng)建沒有分配角色的用戶。
換句話說,它“要么全有,要么全沒有”。
現(xiàn)在我們對事務是什么以及它們實現(xiàn)了什么有了一個簡單的概念,讓我們來看看如何在 Laravel 中使用它們。
在 Laravel 中,由于我們可以在 DB
門面上訪問 transaction()
方法,因此開始使用事務實際上是很容易的事。繼續(xù)使用之前的示例代碼,讓我們看看在創(chuàng)建用戶并為其分配角色時如何使用事務。
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());
});
現(xiàn)在我們的代碼被包裹在一個數(shù)據(jù)庫事務中,如果在其中的任意一點拋出異常,對數(shù)據(jù)庫的任何更改都將返回到事務開始之前的狀態(tài)。
有時,您可能希望對事務進行更精細的控制。例如,假設您正在與第三方服務集成,比如 Mailchinp 或 Xero。我們會說,每當您創(chuàng)建一個新用戶時,您還需要向他們的 API 發(fā)出 HTTP 請求,以將他們也創(chuàng)建為該系統(tǒng)中的用戶。
我們可能想要更新我們的代碼,以便如果我們無法在我們自己的系統(tǒng) ** 且 ** 在第三方系統(tǒng)中創(chuàng)建用戶,則兩個系統(tǒng)都不創(chuàng)建用戶。 如果您正在與第三方系統(tǒng)交互,那么您可能有一個可用于發(fā)出請求的類。 或者,可能有一個您可以使用的包。 有時,當某些請求無法完成時,發(fā)出請求的類可能會拋出異常。 然而,其中一些類可能會消除錯誤,而只是從您調(diào)用的方法中返回 false
,并將錯誤放置在類的字段中。
因此,我們假設我們有以下調(diào)用 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());
查看上面的代碼,我們可以看到我們啟動了一個事務,創(chuàng)建了用戶并為他們分配了一個角色,然后我們調(diào)用了第三方服務。如果在外部服務中成功創(chuàng)建了用戶,知道所有內(nèi)容都已正確創(chuàng)建,我們就可以安全地提交數(shù)據(jù)庫更改。但是,如果沒有在外部服務中創(chuàng)建用戶,則回滾數(shù)據(jù)庫中的更改(刪除用戶及其角色分配),然后報告錯誤。
作為一個額外的技巧,我通常建議將任何影響第三方系統(tǒng)、文件存儲或緩存的代碼放在數(shù)據(jù)庫調(diào)用之后。
為了更深入地理解這一點,讓我們以上面的代碼示例為例。請注意,在向第三方服務發(fā)出請求之前,我們是如何首先對數(shù)據(jù)庫進行所有更改的。這意味著,如果從第三方請求返回任何錯誤,將回滾我們自己數(shù)據(jù)庫中的用戶和角色分配。
然而, 如果我們反過來做,我們在修改數(shù)據(jù)庫之前發(fā)出請求,那就不是這種情況了。出于任何原因,如果我們在數(shù)據(jù)庫中創(chuàng)建用戶時發(fā)生任何錯誤,我們會在第三方系統(tǒng)中創(chuàng)建一個新用戶,但是在我們系統(tǒng)中卻沒有創(chuàng)建。如你所想, 這可能會導致更多問題。通過編寫一個清理方法將用戶從第三方系統(tǒng)中刪除,可以降低這個問題的嚴重性。 但是,正如您可以想象的那樣, 這可能會導致更多的問題,并導致編寫、維護和測試更多的代碼。
所以,我總是建議把數(shù)據(jù)庫調(diào)用放在API調(diào)用之前。但并不總是這樣,有時可能需要將第三方請求返回的值保存到數(shù)據(jù)庫中。如果是這種情況,就需要API調(diào)用放到數(shù)據(jù)庫調(diào)用之前了,只要您確保有一些代碼可以處理任何失敗,這是完全可以的。
同樣值得注意的是,因為我們最初的示例使用DB:transaction()
方法,在拋出異常時回滾事務,所以我們也可以使用這種方法向我們的第三方服務發(fā)出請求。相反,我們可以這樣更新類:
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個用戶數(shù)據(jù)的 CSV 文件,您可能會發(fā)現(xiàn)拋出異常會大大減慢導入速度。
但是,如果它只是在一個用戶可以注冊的簡單web請求中使用,那么拋出異常可能沒有問題。當然,這取決于應用程序的大小,性能是關鍵因素;所以你需要根據(jù)具體情況來決定。
每當您在事務中處理隊列時,您都需要注意一個“陷阱”。
為了提供一些上下文,讓我們繼續(xù)使用之前的代碼示例。我們可以想象,在我們創(chuàng)建了我們的用戶之后,我們想要運行一個任務來提醒管理員通知他們新注冊并向新用戶發(fā)送歡迎電子郵件。我們將通過分派一個名為 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);
});
當您開始一個事務并對其中的任何數(shù)據(jù)進行更改時,這些更改僅對正在運行事務的請求/進程可用。對于任何其他訪問您更改的數(shù)據(jù)的請求或進程,必須先提交事務。因此,這意味著如果我們從事務內(nèi)部分派任何排隊的隊列、事件監(jiān)聽器、郵件,通知或廣播事件。由于競爭條件,我們的數(shù)據(jù)更改可能在事務內(nèi)部不可用。
如果隊列在事務提交之前開始處理排隊的代碼,就會發(fā)生這種情況。因此,這可能導致您的排隊代碼可能試圖訪問不存在的數(shù)據(jù),并可能導致錯誤。在我們的例子中,如果在事務提交之前運行隊列AlertNewUser
作業(yè),那么我們的作業(yè)將嘗試訪問一個尚未實際存儲在數(shù)據(jù)庫中的用戶。如您所料,這將導致作業(yè)失敗。
為了防止這種競爭條件的發(fā)生,我們可以對我們的代碼和/或我們的配置進行一些更改,以確保僅在事務成功提交后才調(diào)度隊列。
我們可以更新 config/queue.php
并添加 after commit
字段。讓我們想象一下,我們正在使用 redis
隊列驅(qū)動程序,所以我們可以這樣更新配置:
[
// ...
'redis' => [
'driver' => 'redis',
// ...
'after_commit' => true,
],
// ...
],
// ...
];
通過進行此更改,如果我們嘗試在事務內(nèi)調(diào)度隊列,則隊列將在實際調(diào)度隊列之前等待事務提交。 方便的是,如果事務回滾,它也會阻止隊列被調(diào)度。
然而,可能有一個原因,您不希望在配置中全局設置此選項。 如果是這種情況,Laravel 仍然提供了一些很好的助手方法,我們可以根據(jù)具體情況使用它們。
如果我們想更新事務中的代碼,只在任務提交后才分派任務,可以使用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代碼中如何正確使用數(shù)據(jù)庫事務”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
分享題目:Laravel代碼中如何正確使用數(shù)據(jù)庫事務
網(wǎng)址分享:http://jinyejixie.com/article2/iissoc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站改版、商城網(wǎng)站、網(wǎng)站維護、微信小程序、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)