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