Giao diện
Events
Giới thiệu (Introduction)
Events trong Laravel triển khai mẫu thiết kế Observer Pattern, cho phép subscribe và listen các events xảy ra trong ứng dụng.
- Event classes nằm trong
app/Events - Listener classes nằm trong
app/Listeners
Events giúp decouple (tách rời) các khía cạnh của ứng dụng — một event có thể có nhiều listeners không phụ thuộc nhau.
Tạo Events và Listeners (Generating)
bash
php artisan make:event PodcastProcessed
php artisan make:listener SendPodcastNotification --event=PodcastProcessedĐăng ký Events và Listeners (Registering)
Event Discovery
Laravel tự động phát hiện listeners bằng cách quét thư mục app/Listeners. Listener class có method handle với type-hint event sẽ tự động được đăng ký.
Đăng ký thủ công
Trong AppServiceProvider:
php
use App\Events\PodcastProcessed;
use App\Listeners\SendPodcastNotification;
use Illuminate\Support\Facades\Event;
public function boot(): void
{
Event::listen(
PodcastProcessed::class,
SendPodcastNotification::class,
);
}Closure Listeners
php
use App\Events\PodcastProcessed;
use Illuminate\Support\Facades\Event;
Event::listen(function (PodcastProcessed $event) {
// ...
});
// Queueable closure
Event::listen(queueable(function (PodcastProcessed $event) {
// ...
}));Định nghĩa Events (Defining Events)
Event class là data container chứa thông tin liên quan:
php
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(
public Order $order,
) {}
}Định nghĩa Listeners (Defining Listeners)
php
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
class SendShipmentNotification
{
public function __construct() {}
/**
* Handle event.
*/
public function handle(OrderShipped $event): void
{
// Truy cập order qua $event->order...
}
}Dừng Event Propagation
Return false từ method handle để ngăn event tiếp tục truyền đến listeners khác:
php
public function handle(OrderShipped $event): bool
{
return false; // Ngăn listeners khác nhận event
}Queued Event Listeners
Listeners có thể chạy trên queue — hữu ích khi thực hiện tác vụ chậm (gửi email, HTTP request):
php
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
// Tự động đẩy vào queue
}Tùy chỉnh Queue Connection / Name
php
class SendShipmentNotification implements ShouldQueue
{
public $connection = 'sqs';
public $queue = 'listeners';
public $delay = 60; // delay 60 giây
}Conditional Queuing
php
public function shouldQueue(OrderShipped $event): bool
{
return $event->order->subtotal >= 5000;
}Xử lý Failed Jobs
php
use Illuminate\Queue\Events\JobFailed;
public function failed(OrderShipped $event, Throwable $exception): void
{
// Xử lý khi listener fail...
}Queued Listeners và Database Transactions
Nếu listener dispatch trong DB transaction, nó có thể chạy trước khi transaction commit. Dùng ShouldHandleEventsAfterCommit:
php
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class SendShipmentNotification implements ShouldQueue, ShouldHandleEventsAfterCommit
{
// Chỉ chạy sau khi transaction commit
}Dispatch Events
php
use App\Events\OrderShipped;
OrderShipped::dispatch($order);
// Hoặc
event(new OrderShipped($order));Dispatch sau Database Transactions
php
OrderShipped::dispatchAfterCommit($order);Deferring Events
Hoãn dispatch events cho đến khi response được gửi:
php
use Illuminate\Support\Defer;
Defer::dispatch(new OrderShipped($order));Event Subscribers
Viết Subscriber
Subscriber class lắng nghe nhiều events:
php
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
class UserEventSubscriber
{
public function handleUserLogin(Login $event): void {}
public function handleUserLogout(Logout $event): void {}
/**
* Đăng ký listeners cho subscriber.
*/
public function subscribe(Dispatcher $events): array
{
return [
Login::class => 'handleUserLogin',
Logout::class => 'handleUserLogout',
];
}
}Đăng ký Subscribers
php
use App\Listeners\UserEventSubscriber;
use Illuminate\Support\Facades\Event;
Event::subscribe(UserEventSubscriber::class);Testing
Fake Events
php
use App\Events\OrderShipped;
use Illuminate\Support\Facades\Event;
Event::fake();
// Thực hiện action...
Event::assertDispatched(OrderShipped::class);
Event::assertDispatched(OrderShipped::class, function ($event) {
return $event->order->id === 1;
});
Event::assertNotDispatched(AnotherEvent::class);
Event::assertNothingDispatched();Fake Subset
php
Event::fake([OrderShipped::class]);
// Chỉ OrderShipped bị fake, events khác vẫn dispatch bình thường