# Laravel Events và Listeners năm 2026: Kiến trúc Event-Driven và Câu hỏi Phỏng vấn > Hướng dẫn toàn diện về Laravel events và listeners cho kiến trúc event-driven. Tìm hiểu observer pattern, queued listeners, event subscribers và câu hỏi phỏng vấn kỹ thuật. - Published: 2026-08-20 - Updated: 2026-08-20 - Author: Anthony Fillion-Maillet - Tags: laravel, events, listeners, event-driven, observer-pattern, queue - Reading time: 12 min --- Laravel events và listeners triển khai [observer pattern](https://refactoring.guru/design-patterns/observer), tách rời các component bằng cách cho phép các phần của ứng dụng phản ứng với các hành động mà không cần phụ thuộc trực tiếp. Khi người dùng đặt hàng, event `OrderShipped` được kích hoạt, và các listener riêng biệt xử lý thông báo, phân tích và cập nhật kho hàng một cách độc lập. > **Laravel 12 Event Discovery** > > Laravel 12 tự động phát hiện các listener trong `app/Listeners`. Bất kỳ class nào có method `handle` hoặc `__invoke` với type-hint đến class event sẽ được đăng ký tự động, không cần file cấu hình. ## Cơ chế Event Discovery hoạt động trong Laravel 12 Từ Laravel 11, `EventServiceProvider` đã bị loại bỏ. Laravel quét thư mục `Listeners` và sử dụng reflection để xác định event nào mà mỗi listener phản hồi dựa trên type hint trong chữ ký method `handle`. ```php // app/Listeners/SendOrderConfirmation.php order->user } } ``` Listener trên được tự động đăng ký cho event `OrderShipped`. Không cần binding thủ công trong service provider. Điều này giảm boilerplate và giữ mối quan hệ event-listener gần với code triển khai. Để xác minh các listener đã đăng ký, chạy `php artisan event:list`. Cho production, cache manifest event với `php artisan event:cache` để bỏ qua việc quét thư mục trên mỗi request. ## Tạo Events và Listeners với Artisan Laravel cung cấp các lệnh Artisan để tạo scaffold events và listeners: ```bash # Tạo class event php artisan make:event OrderShipped # Tạo listener liên kết với event php artisan make:listener SendShipmentNotification --event=OrderShipped ``` Class event được tạo sử dụng ba trait: `Dispatchable` cho method static `dispatch()`, `InteractsWithSockets` cho broadcasting, và `SerializesModels` để serialize các model Eloquent khi listener được queue. ```php // app/Events/OrderShipped.php route('orders.index'); } } ``` Để dispatch có điều kiện, sử dụng `dispatchIf()` hoặc `dispatchUnless()`: ```php // Chỉ dispatch nếu đơn hàng có số tracking OrderShipped::dispatchIf($order->tracking_number !== null, $order); ``` ## Queued Listeners cho các thao tác chậm Các listener gửi email, gọi API bên ngoài, hoặc thực hiện tính toán nặng nên chạy bất đồng bộ. Triển khai `ShouldQueue` để đẩy listener vào queue: ```php // app/Listeners/SendShipmentNotification.php order->user } } ``` Tùy chỉnh kết nối và tên queue với các thuộc tính `$connection` và `$queue`, hoặc sử dụng các method runtime: ```php public function viaConnection(): string { return 'redis'; } public function viaQueue(): string { return $this->event->order->priority === 'high' ? 'high-priority' : 'default'; } ``` ### Conditional Queueing với shouldQueue Đôi khi listener chỉ cần queue dựa trên dữ liệu event: ```php public function shouldQueue(OrderShipped $event): bool { // Chỉ queue cho đơn hàng trên 2.000.000 VND return $event->order->total >= 2000000; } ``` ## Database Transactions và Event Timing Queued listener được dispatch bên trong transaction có thể xử lý trước khi transaction commit, gây ra lỗi dữ liệu không tìm thấy. Có hai giải pháp: **1. Cấu hình toàn cục** trong `config/queue.php`: ```php 'connections' => [ 'redis' => [ 'after_commit' => true, ], ], ``` **2. Theo từng listener** với `ShouldQueueAfterCommit`: ```php use Illuminate\Contracts\Queue\ShouldQueueAfterCommit; class UpdateInventory implements ShouldQueueAfterCommit { public function handle(OrderShipped $event): void { // An toàn để query đơn hàng, transaction đã commit } } ``` Đối với event, triển khai `ShouldDispatchAfterCommit` trên class event: ```php use Illuminate\Contracts\Events\ShouldDispatchAfterCommit; class OrderShipped implements ShouldDispatchAfterCommit { // Event chỉ dispatch sau khi transaction commit } ``` ## Unique Listeners để ngăn chặn trùng lặp Interface `ShouldBeUnique` ngăn các listener trùng lặp vào queue khi một cái đang xử lý: ```php use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; class ProcessLicenseKey implements ShouldQueue, ShouldBeUnique { public $uniqueFor = 3600; // Lock hết hạn sau 1 giờ public function uniqueId(LicensePurchased $event): string { return 'license:' . $event->license->id; } public function handle(LicensePurchased $event): void { // Tạo và gán license key } } ``` Nếu cùng license ID kích hoạt nhiều event nhanh chóng, chỉ listener đầu tiên xử lý. Các lần thử tiếp theo tìm thấy lock đang được giữ và bỏ qua queueing. ## Event Subscribers cho các Events liên quan Khi nhiều event liên quan chia sẻ logic xử lý chung, event subscriber nhóm các handler trong một class duy nhất: ```php // app/Listeners/UserActivitySubscriber.php 'handleLogin', Logout::class => 'handleLogout', ]; } } ``` Subscriber tuân theo quy tắc automatic discovery. Nếu các method handler nằm trong class subscriber với tham số được type-hint đúng cách, Laravel đăng ký chúng tự động. ## Lắng nghe Multiple Events với Union Types Một listener có thể phản hồi nhiều loại event sử dụng PHP 8 union types: ```php public function handle(OrderShipped|OrderCancelled $event): void { match (true) { $event instanceof OrderShipped => $this->notifyShipped($event), $event instanceof OrderCancelled => $this->notifyCancelled($event), }; } ``` Pattern này hoạt động tốt cho các thông báo tuân theo logic tương tự qua các loại event khác nhau. ## Trì hoãn Events với Event::defer Laravel 12 giới thiệu `Event::defer()` để trì hoãn dispatch event cho đến khi khối code hoàn thành. Điều này đảm bảo listener có quyền truy cập vào tất cả các record liên quan: ```php use Illuminate\Support\Facades\Event; Event::defer(function () { $user = User::create(['name' => 'John']); $user->posts()->create(['title' => 'Welcome']); }); // Event cho cả việc tạo User và Post dispatch ở đây ``` Nếu exception xảy ra bên trong closure, các event bị trì hoãn sẽ không bao giờ dispatch. ## Câu hỏi Phỏng vấn về Laravel Events và Listeners Các cuộc phỏng vấn kỹ thuật thường xuyên kiểm tra sự hiểu biết về kiến trúc event-driven. Dưới đây là các câu hỏi phân biệt developer Laravel có kinh nghiệm: **Q: Laravel 12 đăng ký event listener như thế nào mà không có EventServiceProvider?** Laravel quét `app/Listeners` sử dụng reflection. Nó đọc type hint trên method `handle()` hoặc `__invoke()` để xác định event nào mỗi listener phản hồi. Cache manifest trong production với `php artisan event:cache`. **Q: Khi nào listener nên triển khai ShouldQueue?** Bất cứ khi nào thao tác mất hơn vài mili giây: gửi email, gọi API bên ngoài, tạo báo cáo, hoặc xử lý file. Listener đồng bộ chặn HTTP response. **Q: ShouldQueueAfterCommit giải quyết vấn đề gì?** Queued listener có thể xử lý trước khi database transaction commit. Nếu listener query record vừa tạo, nó sẽ thất bại vì dữ liệu chưa được commit. `ShouldQueueAfterCommit` trì hoãn dispatch queue cho đến khi transaction hoàn thành. **Q: Làm thế nào để ngăn xử lý trùng lặp cho cùng một event?** Triển khai `ShouldBeUnique`. Định nghĩa `uniqueId()` để trả về cache key dựa trên dữ liệu event. Listener lấy atomic lock, và các dispatch trùng lặp bị bỏ qua nếu lock đang được giữ. **Q: Sự khác biệt giữa event listener và event subscriber là gì?** Listener xử lý một loại event. Subscriber là class đăng ký các handler cho nhiều event liên quan ở một nơi, hữu ích cho việc nhóm các event xác thực hoặc audit logging. ## Ý nghĩa của Laravel Events cho các ứng dụng có khả năng mở rộng - Events tách rời các component, cho phép các tính năng như thông báo và phân tích phát triển độc lập - Queued listener chuyển các thao tác chậm sang queue, giữ HTTP response nhanh - `ShouldQueueAfterCommit` ngăn race condition giữa queue worker và database transaction - Automatic discovery trong Laravel 12 loại bỏ overhead cấu hình, giữ logic listener gần với triển khai - Event subscriber hợp nhất việc xử lý event liên quan, giảm sự phân tán file cho các pattern phổ biến như theo dõi hoạt động người dùng - Module `/technologies/laravel/interview-questions/events-listeners` trên [SharpSkill](/technologies/laravel/interview-questions/events-listeners) bao gồm các pattern này với câu hỏi tương tác --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/laravel/laravel-events-listeners-event-driven-architecture