Laravel 12 Events Explained: Real-World Examples & Best Practices

Author

Kritim Yantra

Apr 04, 2025

Laravel 12 Events Explained: Real-World Examples & Best Practices

Laravel's event system is a powerful tool that helps you build decoupled, maintainable applications. In this deep dive, we'll explore Laravel 12's event handling capabilities through practical examples you can apply directly to your projects.

Why Use Events? The Power of Loose Coupling

Imagine building an e-commerce platform. When an order is placed, you need to:

  1. Update inventory
  2. Send confirmation emails
  3. Generate an invoice
  4. Notify the logistics team

Without events, your order processing code would quickly become a tangled mess. Events let you handle these actions cleanly:

OrderPlaced::dispatch($order);

Getting Started: Creating Events and Listeners

Example 1: User Registration Flow

Generate the event:

php artisan make:event UserRegistered

Create a listener:

php artisan make:listener SendWelcomeEmail --event=UserRegistered

Event Class:

namespace App\Events;

use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;

class UserRegistered
{
    use Dispatchable;

    public function __construct(public User $user) {}
}

Listener Class:

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Mail\WelcomeEmail;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail
{
    public function handle(UserRegistered $event)
    {
        Mail::to($event->user->email)->send(new WelcomeEmail($event->user));
    }
}

Advanced Registration Techniques

Automatic Discovery vs Manual Registration

Laravel 12 automatically discovers listeners in app/Listeners, but you might need manual registration for:

  1. Third-party package listeners
  2. Conditional listener registration
// In AppServiceProvider
public function boot()
{
    Event::listen(
        UserRegistered::class,
        [SendWelcomeEmail::class, 'handle']
    );
    
    // Conditional listener registration
    if (config('app.send_welcome_emails')) {
        Event::listen(/* ... */);
    }
}

Queued Listeners: Power Up Performance

Example: Video Processing

namespace App\Listeners;

use App\Events\VideoUploaded;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessVideo implements ShouldQueue
{
    public $queue = 'videos';
    public $delay = 60; // Wait 1 minute before processing

    public function handle(VideoUploaded $event)
    {
        // Intensive video processing logic
        $event->video->process();
    }

    public function failed(VideoUploaded $event, Throwable $exception)
    {
        // Notify admin of failed processing
        Notification::sendAdmin("Video processing failed: " . $exception->getMessage());
    }
}

Database Transactions and Event Dispatching

Ensure events only fire after successful transactions:

class OrderController extends Controller
{
    public function store()
    {
        DB::transaction(function () {
            $order = Order::create(/* ... */);
            
            // This event will only dispatch if the transaction commits
            OrderCreated::dispatchAfterCommit($order);
        });
    }
}

Event Subscribers: Group Related Logic

User Activity Tracker:

namespace App\Subscribers;

class UserActivitySubscriber
{
    public function handleLogin($event) {/* ... */}
    public function handleLogout($event) {/* ... */}
    public function handleProfileUpdate($event) {/* ... */}

    public function subscribe()
    {
        return [
            Login::class => 'handleLogin',
            Logout::class => 'handleLogout',
            ProfileUpdated::class => 'handleProfileUpdate',
        ];
    }
}

Registration:

Event::subscribe(UserActivitySubscriber::class);

Testing Events Like a Pro

Feature Test Example:

public function test_order_confirmation_email_sent()
{
    Event::fake();

    $user = User::factory()->create();
    
    $response = $this->post('/place-order', [
        // Order details
    ]);

    Event::assertDispatched(OrderPlaced::class, function ($event) use ($user) {
        return $event->order->user_id === $user->id;
    });
    
    Event::assertNotDispatched(PaymentFailed::class);
}

Testing Queued Listeners:

public function test_video_processing_queued()
{
    Bus::fake();
    
    $video = Video::factory()->create();
    
    VideoUploaded::dispatch($video);
    
    Bus::assertDispatched(ProcessVideo::class);
}

Real-World Use Cases

  1. Audit Logging:
class LogUserActivity
{
    public function handle($event)
    {
        ActivityLog::create([
            'user_id' => $event->user->id,
            'event' => get_class($event),
            'data' => $event->getLogData()
        ]);
    }
}
  1. Cache Busting:
class ClearProductCache
{
    public function handle(ProductUpdated $event)
    {
        Cache::forget("product_{$event->product->id}");
        Cache::forget('featured_products');
    }
}
  1. Cross-Service Communication:
class SyncWithCRM implements ShouldQueue
{
    public function handle(CustomerUpdated $event)
    {
        Http::post('crm-api.com/customers', [
            'id' => $event->customer->crm_id,
            'data' => $event->customer->toArray()
        ]);
    }
}

Best Practices and Pro Tips

  1. Naming Conventions:

    • Events: Past tense (UserRegistered, OrderShipped)
    • Listeners: Action-oriented (SendNotification, UpdateInventory)
  2. When to Use Events:

    • Multiple unrelated actions needed
    • Third-party integrations
    • Optional application features
    • Background processing
  3. Performance Considerations:

    • Use queued listeners for slow operations
    • Limit synchronous listeners
    • Monitor listener execution times
  4. Debugging:

    • Use event:list Artisan command
    • Log event dispatching:
      Event::listen('*', function ($eventName, $payload) {
          Log::debug("Event fired: $eventName", $payload);
      });
      

Conclusion

Laravel's event system is your gateway to building scalable, maintainable applications. By implementing events and listeners effectively, you can:

  • Reduce code complexity
  • Improve application performance
  • Create more testable code
  • Easily add new features
  • Integrate with external services seamlessly

Remember: While powerful, events shouldn't replace all direct method calls. Use them judiciously where they provide clear architectural benefits.

Ready to supercharge your Laravel applications? Start by identifying one complex process in your current project that could benefit from event-driven architecture, and refactor it using these techniques!

Tags

Laravel Php

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Continue with Google

Related Posts