Cookbook

Events

Practical patterns for dispatching and handling events in Glueful

This page focuses on event patterns that match the current framework surface.

For standalone snippets below, assume $context is an available ApplicationContext. In controllers, prefer $this->getContext() and your injected services.

When To Use Events

Use events when:

  • multiple parts of the system react to the same action
  • side effects should stay decoupled from the main request
  • some reactions can be queued or retried independently

Use direct service calls when:

  • there is only one consumer
  • the action must complete synchronously
  • introducing fan-out would make the flow harder to reason about

Dispatch An Event

use App\Events\UserRegisteredEvent;
use Glueful\Events\Event;

Event::dispatch(new UserRegisteredEvent(
    userId: $user['uuid'],
    email: $user['email'],
));

Register A Listener

use App\Events\UserRegisteredEvent;
use App\Jobs\SendWelcomeEmailJob;
use Glueful\Events\Event;
use Glueful\Queue\QueueManager;

Event::listen(UserRegisteredEvent::class, function (UserRegisteredEvent $event) use ($context): void {
    $queue = app($context, QueueManager::class);
    $queue->push(SendWelcomeEmailJob::class, [
        'userId' => $event->userId,
        'email' => $event->email,
    ]);
});

Class-Based Listener

namespace App\Listeners;

use App\Events\OrderPlacedEvent;
use Psr\Log\LoggerInterface;

final class LogOrderPlacedListener
{
    public function __construct(
        private LoggerInterface $logger
    ) {}

    public function handle(OrderPlacedEvent $event): void
    {
        $this->logger->info('Order placed', [
            'order_id' => $event->orderId,
            'total' => $event->total,
        ]);
    }
}

Register it:

use App\Events\OrderPlacedEvent;
use App\Listeners\LogOrderPlacedListener;
use Glueful\Events\Event;

Event::listen(OrderPlacedEvent::class, [LogOrderPlacedListener::class, 'handle']);

Event Subscriber

Use a subscriber when several listeners belong to the same domain.

namespace App\Listeners;

use App\Events\UserDeletedEvent;
use App\Events\UserRegisteredEvent;
use Glueful\Events\EventSubscriberInterface;

final class UserLifecycleSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UserRegisteredEvent::class => 'onRegistered',
            UserDeletedEvent::class => 'onDeleted',
        ];
    }

    public function onRegistered(UserRegisteredEvent $event): void
    {
        // send welcome flow
    }

    public function onDeleted(UserDeletedEvent $event): void
    {
        // clean up downstream state
    }
}

Queue Heavy Work

Keep listeners fast. If a listener sends email, generates files, or calls external APIs, hand that work to the queue:

use Glueful\Queue\QueueManager;

$queue = app($context, QueueManager::class);
$queue->push(ProcessImageJob::class, [
    'imageUuid' => $event->imageUuid,
]);

Add Logging

use Psr\Log\LoggerInterface;

$logger = app($context, LoggerInterface::class);
$logger->info('User logged in', [
    'user_id' => $event->userId,
    'provider' => $event->provider,
]);

Create Event Classes

Use the CLI scaffolds that exist today:

php glueful event:create UserRegistered
php glueful event:listener SendWelcomeEmail

Practical Pattern

use App\Events\PasswordResetRequestedEvent;
use Glueful\Events\Event;
use Glueful\Queue\QueueManager;

Event::listen(PasswordResetRequestedEvent::class, function (PasswordResetRequestedEvent $event) use ($context): void {
    app($context, QueueManager::class)->push(SendPasswordResetEmailJob::class, [
        'email' => $event->email,
        'token' => $event->token,
    ]);
});

This keeps the controller focused on validation and response generation while the side effect runs separately.

Best Practices

  • keep event payloads small and explicit
  • prefer immutable event data
  • do not hide critical synchronous behavior behind listeners
  • queue expensive listeners instead of blocking the request
  • log listener failures with enough context to retry safely