Series Laravel Email & Mailtrap #4: Tách rời Logic bằng Event & Listener (Trạm cuối)
1. Kiến trúc Publish/Subscribe (Pub/Sub)
Trong Laravel, Event là một sự kiện xảy ra trong hệ thống (ví dụ: TicketPurchased - Vé đã được mua). Listener là những kẻ ngồi rình xem sự kiện đó bao giờ xảy ra thì lao vào làm việc của mình (ví dụ: SendTicketEmail, SendSmsNotification).
Nhờ cơ chế này, thằng tạo ra sự kiện (Controller) hoàn toàn mù tịt, không cần biết có bao nhiêu thằng Listener đang lắng nghe mình. Kiến trúc hệ thống AFC Metro của bạn sẽ trở nên cực kỳ lỏng lẻo (Decoupled) và dễ mở rộng.
2. Khởi tạo Event và Listener
Hãy mở Terminal và gõ 2 lệnh sau để nhờ Laravel tạo sẵn bộ khung:
# 1. Tạo sự kiện "Đã mua vé"
php artisan make:event TicketPurchased
# 2. Tạo người lắng nghe "Gửi email vé" và trỏ nó vào sự kiện trên
php artisan make:listener SendTicketEmail --event=TicketPurchased
Lúc này, Laravel đã sinh ra cho bạn 2 file trong thư mục app/Events và app/Listeners.
3. Cấu hình Event (Sự kiện)
Sự kiện làm nhiệm vụ chuyên chở dữ liệu. Khi sự kiện "Vé đã mua" xảy ra, nó phải mang theo thông tin của cái vé đó để đưa cho các Listener.
Mở file app/Events/TicketPurchased.php và thêm một biến để lưu dữ liệu:
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TicketPurchased
{
use Dispatchable, SerializesModels;
public array $ticketData;
/**
* Nhận dữ liệu vé khi sự kiện được kích hoạt
*/
public function __construct(array $ticketData)
{
$this->ticketData = $ticketData;
}
}
4. Cấu hình Listener (Người lắng nghe)
Mở file app/Listeners/SendTicketEmail.php. Đây là nơi chúng ta sẽ bứng cái đoạn code gọi Mail từ trong Controller ra. Khi sự kiện xảy ra, hàm handle() sẽ tự động được chạy.
namespace App\Listeners;
use App\Events\TicketPurchased;
use Illuminate\Support\Facades\Mail;
use App\Mail\WelcomeEmail;
class SendTicketEmail
{
public function __construct()
{
//
}
/**
* Xử lý sự kiện: Nhận dữ liệu từ Event và bắn Email
*/
public function handle(TicketPurchased $event): void
{
// $event->ticketData chính là cục dữ liệu ta đã truyền vào ở trên
$data = $event->ticketData;
$customerEmail = $data['email'] ?? 'khachhang_test@gmail.com';
// Gọi Queue để ném email vào hàng đợi như Bài 3
Mail::to($customerEmail)->queue(new WelcomeEmail($data));
}
}
Lưu ý (Chỉ dành cho Laravel 10 trở xuống): Bạn cần đăng ký cặp Event-Listener này trong file app/Providers/EventServiceProvider.php ở mảng $listen. (Nếu dùng Laravel 11, tính năng Auto-discovery sẽ tự động tìm thấy chúng, bạn không cần làm gì thêm).
5. Kích hoạt Sự kiện tại Controller
Mọi thứ đã kết nối xong. Bây giờ, hãy quay lại Controller hoặc routes/web.php để xem sự ma thuật của Clean Code.
use Illuminate\Support\Facades\Route;
use App\Events\TicketPurchased;
use Illuminate\Support\Str;
Route::get('/test-email', function () {
// 1. Controller xử lý nghiệp vụ lưu Database, tính tiền...
$data = [
'name' => 'Hiếu Nguyễn',
'email' => 'hieu.nguyen@example.com',
'station' => 'Ga Bến Thành',
'price' => 15000,
'ticket_code' => strtoupper(Str::random(8))
];
// 2. PHÁT LOA THÔNG BÁO: Kích hoạt sự kiện
// Thay vì gọi Mail::to(), ta chỉ cần dispatch Event
event(new TicketPurchased($data));
return 'Giao dịch thành công! Các hệ thống ngầm tự động phân chia công việc.';
});
Chỉ một dòng event(...) duy nhất! Controller của bạn giờ đây đã rũ bỏ hoàn toàn trách nhiệm gửi email. Sau này cần gửi thêm SMS, bạn chỉ việc tạo thêm một file SendSmsNotification.php lắng nghe cùng một sự kiện TicketPurchased, và Controller vẫn không thay đổi một dấu phẩy nào.
Tổng kết Series Email & Mailtrap
Hành trình xây dựng hệ thống gửi thư điện tử của chúng ta đã chính thức hoàn thiện.
- Bạn đã biết dùng Mailtrap để test an toàn không sợ gửi nhầm khách hàng.
- Bạn đã biết đúc Mailable để đổ dữ liệu động và đính kèm hóa đơn PDF.
- Bạn đã áp dụng Queue để giải phóng Server, đưa tốc độ phản hồi API về 0 độ trễ.
- Bạn đã dùng Event/Listener để thiết kế một kiến trúc mở, cô lập hoàn toàn logic thông báo khỏi luồng xử lý giao dịch.
Với những kỹ thuật này, hệ thống Backend của bạn đã hội tụ đủ các yếu tố: Nhanh, Bền Bỉ, và Sạch Sẽ. Chúc bạn vận dụng thành công những mảnh ghép này vào các hệ thống trạm thu phí sắp tới!
All rights reserved