Events & Listeners
This document describes all the events available in the Pollora framework and how to listen to them.
Table of Contents
Section titled “Table of Contents”Framework Basics
Section titled “Framework Basics”WordPress Integration
Section titled “WordPress Integration”Development Guide
Section titled “Development Guide”Introduction
Section titled “Introduction”Events provide a simple observer pattern implementation, allowing you to subscribe and listen for various events that occur in your WordPress application. Event classes are typically stored in the app/Events directory, while their listeners are stored in app/Listeners.
What are Events?
Section titled “What are Events?”Events serve as a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other. For example, you might want to send a Slack notification and queue a task when a post is published. Instead of coupling these actions to your post publishing code, you can raise a PostPublished event which your listeners can handle.
Laravel Events vs WordPress Hooks
Section titled “Laravel Events vs WordPress Hooks”While WordPress provides its own hooks system (actions and filters), Laravel’s event system offers several advantages:
- Type Safety: Events are proper PHP classes, providing better IDE support and type hinting
- Dependency Injection: Listeners can use Laravel’s service container for dependencies
- Queueing: Listeners can be queued for better performance
- Testing: Laravel provides tools for testing events and listeners
- Organization: Events and listeners are organized in a more structured way
The Pollora framework bridges these two worlds by automatically converting some useful WordPress hooks into Laravel events, giving you the best of both ecosystems.
Event & Listener Implementation
Section titled “Event & Listener Implementation”Defining Events
Section titled “Defining Events”Events are typically simple classes that hold the data related to the event. For example:
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;use Illuminate\Queue\SerializesModels;
class PostPublished{ use Dispatchable, SerializesModels;
public $post;
public function __construct($post) { $this->post = $post; }}Defining Listeners
Section titled “Defining Listeners”Listeners are classes that define the handling logic for events:
namespace App\Listeners;
class SendPostPublishedNotification{ /** * Create the event listener. */ public function __construct() { // Constructor injection is supported }
/** * Handle the event. */ public function handle(PostPublished $event) { // Access the post using $event->post // Handle the event... }}Event Discovery
Section titled “Event Discovery”By default, Laravel will automatically discover and register event listeners by scanning your application’s Listeners directory. You can disable this in your EventServiceProvider:
/** * Determine if events and listeners should be automatically discovered. */public function shouldDiscoverEvents(): bool{ return false;}Event Subscribers
Section titled “Event Subscribers”Event subscribers are classes that may subscribe to multiple events from within the class itself. Subscribers should define a subscribe method that will be passed an event dispatcher instance:
namespace App\Listeners;
use Illuminate\Events\Dispatcher;
class WordPressEventSubscriber{ public function handlePostCreated($event) {} public function handlePostUpdated($event) {} public function handleUserCreated($event) {}
public function subscribe(Dispatcher $events): array { return [ 'Pollora\Events\WordPress\Post\PostCreated' => 'handlePostCreated', 'Pollora\Events\WordPress\Post\PostUpdated' => 'handlePostUpdated', 'Pollora\Events\WordPress\User\UserCreated' => 'handleUserCreated', ]; }}Advanced Queue Configuration
Section titled “Advanced Queue Configuration”When using queued listeners, you can customize the queue connection and queue name:
use Illuminate\Contracts\Queue\ShouldQueue;
class SendPostPublishedNotification implements ShouldQueue{ public $connection = 'redis'; public $queue = 'listeners'; public $delay = 60; // seconds public $tries = 3; public $backoff = [2, 5, 10]; // retry delays in seconds
public function handle($event) { // Handle the event... }
public function failed($event, $exception) { // Handle failed job... }}Dispatching Events
Section titled “Dispatching Events”You can dispatch events using several methods:
// Using the Event facadeuse Illuminate\Support\Facades\Event;Event::dispatch(new PostPublished($post));
// Using the event helperevent(new PostPublished($post));
// Using the Dispatchable traitPostPublished::dispatch($post);WordPress Integration
Section titled “WordPress Integration”WordPress Hooks to Laravel Events
Section titled “WordPress Hooks to Laravel Events”The Pollora framework automatically converts some WordPress hooks into Laravel events, allowing you to use Laravel’s powerful event system with WordPress. Here’s how it works:
- WordPress actions and filters are intercepted by the framework
- They are converted into Laravel events
- Your Laravel event listeners are triggered
- The event data is passed back to WordPress if needed
Event Naming Convention
Section titled “Event Naming Convention”WordPress events in Pollora follow a consistent naming convention:
- Post events:
Pollora\Events\WordPress\Post\Post{Action} - User events:
Pollora\Events\WordPress\User\User{Action} - Media events:
Pollora\Events\WordPress\Media\Media{Action} - And so on…
WordPress Data in Events
Section titled “WordPress Data in Events”All WordPress events include the relevant WordPress objects (WP_Post, WP_User, etc.) as properties of the event class. This allows you to access WordPress data in a Laravel-friendly way:
use Pollora\Events\WordPress\Post\PostCreated;
class PostCreatedListener{ public function handle(PostCreated $event) { $post = $event->post; // WP_Post instance $title = $post->post_title; $content = $post->post_content; // ... }}Best Practices
Section titled “Best Practices”Event Design
Section titled “Event Design”-
Keep Events Simple
- Events should primarily hold data
- Avoid including business logic in event classes
- Use meaningful names that describe what happened
-
Event Properties
- Make properties public for easy access
- Use type hints for better IDE support
- Use
readonlyproperties for immutable event data
class PostPublished{ use Dispatchable, SerializesModels;
public function __construct( public readonly WP_Post $post, public readonly string $oldStatus, public readonly string $newStatus ) {}}Listener Design
Section titled “Listener Design”-
Single Responsibility
- Each listener should do one thing well
- Split complex listeners into multiple simpler ones
- Use dependency injection for services
-
Error Handling
- Always catch and handle exceptions
- Log errors appropriately
- Consider what should happen if the listener fails
class SendPostNotification{ public function __construct( private NotificationService $notifications, private LoggerInterface $logger ) {}
public function handle(PostPublished $event) { try { $this->notifications->send($event->post); } catch (Exception $e) { $this->logger->error('Failed to send notification', [ 'post_id' => $event->post->ID, 'error' => $e->getMessage() ]); } }}- Queue Configuration
- Use queues for time-consuming operations
- Set appropriate timeouts and retry settings
- Handle failed jobs gracefully
class ProcessPostImages implements ShouldQueue{ public $tries = 3; public $backoff = [30, 60, 120]; public $timeout = 120;
public function handle(PostPublished $event) { // Process images... }
public function failed(PostPublished $event, Throwable $e) { Log::error('Image processing failed', [ 'post_id' => $event->post->ID, 'error' => $e->getMessage() ]); }}Debugging Events
Section titled “Debugging Events”Event Listening
Section titled “Event Listening”You can listen to all events fired in your application using the Event::listen() method with a wildcard:
Event::listen('*', function ($event, $data) { Log::info('Event fired:', [ 'event' => $event, 'data' => $data ]);});Event Discovery
Section titled “Event Discovery”To see which events and listeners are registered in your application, use the artisan command:
php artisan event:listQueue Monitoring
Section titled “Queue Monitoring”For queued listeners, you can monitor the queue using Horizon or the built-in queue commands:
# Monitor failed jobsphp artisan queue:failed
# Retry failed jobsphp artisan queue:retry all
# Clear failed jobsphp artisan queue:clearTesting Events
Section titled “Testing Events”Laravel provides several methods to test events:
use Illuminate\Support\Facades\Event;
class PostTest extends TestCase{ public function test_post_created_event_is_dispatched() { Event::fake();
// Perform the action that should fire the event $post = Post::create(['title' => 'Test Post']);
// Assert the event was dispatched Event::assertDispatched(PostCreated::class, function ($event) use ($post) { return $event->post->ID === $post->ID; }); }
public function test_listener_is_called() { Event::fake([PostCreated::class]);
// Create a post $post = Post::create(['title' => 'Test Post']);
// Assert the listener was called Event::assertListening( PostCreated::class, SendPostNotification::class ); }}Performance Considerations
Section titled “Performance Considerations”Event Optimization
Section titled “Event Optimization”- Selective Event Registration
- Only register listeners for events you need
- Use event discovery with caution in production
- Consider disabling unused WordPress hooks
// In your service providerpublic function register(){ // Disable specific WordPress hooks add_filter('some_unused_filter', '__return_false');}- Efficient Event Handling
- Keep event handlers lightweight
- Move heavy processing to queued jobs
- Use caching when appropriate
class ProcessPostImages implements ShouldQueue{ public function handle(PostPublished $event) { // Check cache first if (Cache::has("processed_images.{$event->post->ID}")) { return; }
// Process images $this->processImages($event->post);
// Cache the result Cache::put("processed_images.{$event->post->ID}", true, now()->addDay()); }}Queue Optimization
Section titled “Queue Optimization”- Queue Configuration
- Choose the right queue driver for your needs
- Set appropriate worker configurations
- Monitor queue performance
return [ 'default' => env('QUEUE_CONNECTION', 'redis'), 'connections' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, 'block_for' => null, ], ],];- Horizon Configuration If you’re using Laravel Horizon for queue management:
'environments' => [ 'production' => [ 'supervisor-1' => [ 'connection' => 'redis', 'queue' => ['default'], 'balance' => 'simple', 'processes' => 10, 'tries' => 3, ], ],],Memory Management
Section titled “Memory Management”- Garbage Collection
- Clear event listeners when no longer needed
- Use
forget()to remove specific listeners - Consider cleanup in long-running processes
// Remove specific listenerEvent::forget('Pollora\Events\WordPress\Post\PostCreated');
// Remove all listenersEvent::flush();- Memory Leaks Prevention
- Avoid storing large objects in event properties
- Use references when possible
- Clean up temporary files and resources
class ImageProcessor{ private $tempFiles = [];
public function __destruct() { // Clean up temporary files foreach ($this->tempFiles as $file) { if (file_exists($file)) { unlink($file); } } }}For a complete catalog of available WordPress events, see WordPress Events Reference.