Sử dụng các giao dịch cơ sở dữ liệu là một cách mạnh mẽ để đảm bảo tính toàn vẹn của dữ liệu. Bạn nhóm nhiều truy vấn cơ sở dữ liệu thành một giao dịch, những truy vấn này sẽ chỉ có hiệu lực nếu tất cả chúng đều thành công. Hãy xem xét đoạn mã sau
$user = User::create[[...]];
Team::create[[
'owner_id' => $user->id,
...
]];
Nếu việc tạo nhóm không thành công, một người dùng sẽ không có nhóm trong hệ thống của bạn. Để ngăn điều này xảy ra, bạn có thể bọc khối bên trong một giao dịch
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];
Bây giờ nếu việc tạo nhóm không thành công, toàn bộ giao dịch sẽ bị khôi phục, bao gồm cả truy vấn tạo người dùng. Laravel xử lý mọi thứ đằng sau hậu trường để bạn có thể đảm bảo tính toàn vẹn dữ liệu của mình chỉ bằng một vài dòng mã
Tuy nhiên, các giao dịch cơ sở dữ liệu chỉ bao gồm các truy vấn cơ sở dữ liệu và thực hiện chúng dưới dạng một đơn vị công việc. Bất kỳ mã nào khác mà bạn có thể đưa vào bên trong giao dịch sẽ được thực hiện ngay lập tức và sẽ không đợi sau khi giao dịch được thực hiện
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];
Trong ví dụ trên, email chào mừng sẽ được gửi tới người dùng ngay cả khi việc tạo nhóm không thành công và hồ sơ người dùng không được lưu trữ trong cơ sở dữ liệu
Một cách khắc phục rõ ràng cho vấn đề này là trích xuất mã gửi thư bên ngoài giao dịch
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }]; Mail::to[$user]->send[new WelcomeEmail[]];
Bây giờ nếu giao dịch không thành công, một ngoại lệ sẽ được đưa ra và thư sẽ không được gửi. Tuy nhiên, trong nhiều trường hợp, mã chạy xung quanh các truy vấn cơ sở dữ liệu không được gọi trực tiếp. Ví dụ: việc gửi email có thể nằm trong một trình lắng nghe sự kiện
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];3 kích hoạt sau
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];0. Trong trường hợp đó, việc gửi email sẽ diễn ra ngay sau khi người dùng được tạo và trước khi giao dịch được thực hiện
Bắt đầu từ Laravel
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];1, bạn có thể bọc bất kỳ mã nào bên trong một bao đóng mà sẽ chỉ được gọi sau khi tất cả các giao dịch đã được thực hiện. Vì vậy, bên trong trình lắng nghe sự kiện gửi email, bạn có thể làm điều này
class SendWelcomeEmail{ public function handle[] { DB::afterCommit[function[]{ Mail::to[$user]->send[new WelcomeEmail[]]; }]; } }
Bây giờ khi người dùng được tạo và sự kiện được kích hoạt, người nghe sẽ gọi phương thức
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];2 sẽ đặt logic gửi thư vào bộ đệm cục bộ và sẽ chỉ thực hiện nó sau khi bất kỳ giao dịch cơ sở dữ liệu nào có thể được mở đã được thực hiện
Nhưng hãy đồng ý, việc phải bọc mã bên trong một bao đóng không làm cho nó trông đẹp hơn chút nào. Vì lý do đó, Laravel giới thiệu một cách khác để đảm bảo các bộ lắng nghe của bạn chỉ được thực thi khi các giao dịch cơ sở dữ liệu đã được cam kết. Hãy lấy người nghe
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];3 làm ví dụ
class SendWelcomeEmail{ public $afterCommit = true; public function handle[] { Mail::to[$user]->send[new WelcomeEmail[]]; } }
Sử dụng thuộc tính
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];4, chúng ta có thể hướng dẫn laravel chỉ chạy phương thức
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];5 sau khi bất kỳ giao dịch mở nào được cam kết. Nếu không có giao dịch nào được mở, mã sẽ chạy ngay như bình thường
Một tình huống khác mà điều này có ích là gửi một công việc, thư, thông báo, sự kiện được phát hoặc trình nghe được xếp hàng đợi từ bên trong một giao dịch. Công nhân có thể chọn công việc trước khi giao dịch thực hiện và mã sẽ chạy trên trạng thái cơ sở dữ liệu nơi các bản ghi được sửa đổi bởi giao dịch vẫn ở trạng thái cũ. Ví dụ
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];2
Công việc
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];3 sẽ được gửi đến hàng đợi trước khi giao dịch được thực hiện. Nếu một công nhân chọn nó ngay lập tức, một ngoại lệ
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];7 sẽ được đưa ra vì mô hình Người dùng được chuyển cho công việc chưa được lưu trữ trong cơ sở dữ liệu
Đặt thuộc tính
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];4 thành true trong lớp công việc sẽ đảm bảo công việc chỉ được gửi đi nếu giao dịch đã được cam kết. Bạn cũng có thể đặt
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];9 thành
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];0 trong cấu hình kết nối hàng đợi bên trong tệp cấu hình
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];1
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];9
Bây giờ tất cả các công việc được gửi qua kết nối hàng đợi
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];2 sẽ đợi cho đến khi bất kỳ giao dịch mở nào được thực hiện
Ngoài ra, bạn có thể quyết định hành vi trong khi gửi công việc
DB::transaction[function[]{ $user = User::create[[...]]; Mail::to[$user]->send[new WelcomeEmail[]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];1
Bạn có thể sử dụng thuộc tính
DB::transaction[function[]{ $user = User::create[[...]]; Team::create[[ 'owner_id' => $user->id, ... ]]; }];4 trên mailables, notification, jobs, listener, model observers và Broadcasted events
Nếu bạn muốn tìm hiểu thêm về hệ thống hàng đợi của Laravel, hãy đảm bảo kiểm tra Laravel Queues in Action. Tôi đã đặt mọi thứ tôi biết về hệ thống xếp hàng ở định dạng sách điện tử cùng với một số trường hợp sử dụng thực tế. Hãy xem nó để biết khóa học cấp tốc, sách dạy nấu ăn, hướng dẫn và tài liệu tham khảo