Event & Observer Bridges
Auto-register events, listeners, and observers
Event Bridge
Auto-discovers and registers event listeners and subscribers.
Directory Structure
Modules/Orders/
└── app/
├── Events/
│ ├── OrderPlaced.php
│ └── OrderShipped.php
├── Listeners/
│ ├── SendOrderConfirmation.php
│ └── UpdateInventory.php
└── Subscribers/
└── OrderEventSubscriber.php
Event Class
namespace Modules\Orders\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderPlaced
{
use Dispatchable, SerializesModels;
public function __construct(
public readonly Order $order
) {}
}
Listener Class
Listeners are auto-discovered by their type-hinted handle method:
namespace Modules\Orders\Listeners;
use Modules\Orders\Events\OrderPlaced;
class SendOrderConfirmation
{
public function handle(OrderPlaced $event): void
{
Mail::to($event->order->customer->email)
->send(new OrderConfirmationMail($event->order));
}
}
Event Subscriber
namespace Modules\Orders\Subscribers;
use Illuminate\Events\Dispatcher;
use Modules\Orders\Events\OrderPlaced;
use Modules\Orders\Events\OrderShipped;
class OrderEventSubscriber
{
public function subscribe(Dispatcher $events): array
{
return [
OrderPlaced::class => 'handleOrderPlaced',
OrderShipped::class => 'handleOrderShipped',
];
}
public function handleOrderPlaced(OrderPlaced $event): void
{
// ...
}
public function handleOrderShipped(OrderShipped $event): void
{
// ...
}
}
Configuration
'bridges' => [
'events' => [
'enabled' => true,
],
],
Observer Bridge
Auto-registers model observers.
Directory Structure
Modules/Orders/
└── app/
└── Observers/
└── OrderObserver.php
Observer Class
Name convention: {Model}Observer
namespace Modules\Orders\Observers;
use Modules\Orders\Models\Order;
class OrderObserver
{
public function creating(Order $order): void
{
$order->order_number = $this->generateOrderNumber();
}
public function created(Order $order): void
{
event(new OrderPlaced($order));
}
public function updating(Order $order): void
{
if ($order->isDirty('status')) {
// Status is changing
}
}
public function updated(Order $order): void
{
if ($order->wasChanged('status')) {
event(new OrderStatusChanged($order));
}
}
public function deleted(Order $order): void
{
// Cleanup
}
private function generateOrderNumber(): string
{
return 'ORD-' . now()->format('Ymd') . '-' . Str::random(6);
}
}
Model Resolution
The bridge finds the model by convention:
| Observer | Looks for Model |
|---|---|
OrderObserver |
Modules\Orders\Models\Order |
ProductObserver |
Modules\Products\Models\Product |
UserProfileObserver |
Modules\Users\Models\UserProfile |
Custom Model Binding
If convention doesn’t work, specify the model:
class OrderObserver
{
public static string $model = \Modules\Orders\Models\Order::class;
// ...
}
Configuration
'bridges' => [
'observers' => [
'enabled' => true,
],
],
Policy Bridge
Auto-registers authorization policies.
Directory Structure
Modules/Orders/
└── app/
└── Policies/
└── OrderPolicy.php
Policy Class
Name convention: {Model}Policy
namespace Modules\Orders\Policies;
use App\Models\User;
use Modules\Orders\Models\Order;
class OrderPolicy
{
public function viewAny(User $user): bool
{
return $user->hasPermission('orders.view');
}
public function view(User $user, Order $order): bool
{
return $user->id === $order->customer_id
|| $user->hasPermission('orders.view-any');
}
public function create(User $user): bool
{
return $user->hasPermission('orders.create');
}
public function update(User $user, Order $order): bool
{
return $user->id === $order->customer_id
&& $order->status === 'pending';
}
public function delete(User $user, Order $order): bool
{
return $user->hasPermission('orders.delete');
}
}
Model Resolution
| Policy | Looks for Model |
|---|---|
OrderPolicy |
Modules\Orders\Models\Order |
ProductPolicy |
Modules\Products\Models\Product |
Usage
// In controllers
$this->authorize('view', $order);
$this->authorize('create', Order::class);
// In Blade
@can('update', $order)
<button>Edit</button>
@endcan
// Programmatic
if ($user->can('delete', $order)) {
$order->delete();
}
Configuration
'bridges' => [
'policies' => [
'enabled' => true,
],
],