Skip to content

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