使用數據庫的事務機制是保證數據完整性的有效方法,將幾條數據操作語句放到一個事務組裏,這些語句要麽全部成功,要麽全部失敗。
$user = User::create([...]);
Team::create([
'owner_id' => $user->id,
...
]);
上面的代碼沒有使用事務機制,假如Team::create
執行失敗,那該用戶就會出現無分組(team)的情況。為了避免上述情況發生,可以將它們包含在事務之中:
DB::transaction(function(){
$user = User::create([...]);
Team::create([
'owner_id' => $user->id,
...
]);
});
然後,數據庫的事務機制只是針對數據執行語句實現原子性。而在事務中的其它代碼即使事務未commit,也會正常運行。
DB::transaction(function(){
$user = User::create([...]);
Mail::to($user)->send(new WelcomeEmail());
Team::create([
'owner_id' => $user->id,
...
]);
});
在上面例子中,歡迎郵件(WelcomeEmail)無論用戶team是否創建成功均會發送,這顯然不符合我們的預期。
一般的修復方式是將歡迎郵件移出事務外面。
DB::transaction(function(){
$user = User::create([...]);
Team::create([
'owner_id' => $user->id,
...
]);
});
Mail::to($user)->send(new WelcomeEmail());
現在即使事務失敗,將會拋出異常,不會執行下面的代碼。但是在大多數情況下,代碼不會直接這樣子寫,可能發送郵件的代碼在User::create()
後觸發UserCreated
進行發送,這樣又會出現上面的情況。
在Laravelv8.19.0
開始,可以通過閉包
的方式把代碼包含進去,閉包
裏面的代碼只能在事務全部提交commit
之後才會執行。
class SendWelcomeEmail{
public function handle()
{
DB::afterCommit(function(){
Mail::to($user)->send(new WelcomeEmail());
});
}
}
這個看起來似乎不夠優雅,Laravel還允許你這樣子寫來實現相同功能:
class SendWelcomeEmail{
public $afterCommit = true;
public function handle()
{
Mail::to($user)->send(new WelcomeEmail());
}
}
留意上面類中的$afterCommit
屬性。
接下來看怎麽在事務中使用:
DB::transaction(function(){
$user = User::create([...]);
SendWelcomeEmail::dispatch($user);
Team::create([
'owner_id' => $user->id,
...
]);
});
SendWelcomeEmail
會被分配到隊列中進行處理,隊列配置具體不表。