The EventsPlugin automatically discovers and registers event listeners from all enabled modules, similar to how Laravel’s own event discovery works.
Overview
This plugin scans each module’s src/Listeners directory for event listener classes and automatically registers them with Laravel’s event dispatcher. It respects your application’s event discovery configuration.
Source Location
InterNACHI\Modular\Plugins\EventsPlugin
Activation
The plugin uses the AfterResolving attribute to activate when the event dispatcher is resolved:
#[AfterResolving(Dispatcher::class, parameter: 'events')]
class EventsPlugin extends Plugin
This plugin only runs if event discovery is enabled in your application, either globally or through the package configuration.
How It Works
1. Discovery Check
The plugin first checks if event discovery should run:
protected function shouldDiscoverEvents(): bool
{
return $this->config->get('app-modules.should_discover_events')
?? $this->appIsConfiguredToDiscoverEvents();
}
protected function appIsConfiguredToDiscoverEvents(): bool
{
return collect($this->app->getProviders(EventServiceProvider::class))
->filter(fn(EventServiceProvider $provider) =>
$provider::class === EventServiceProvider::class
|| str_starts_with(get_class($provider), $this->app->getNamespace())
)
->contains(fn(EventServiceProvider $provider) =>
$provider->shouldDiscoverEvents()
);
}
2. Discovery Phase
The discover() method scans listener directories:
public function discover(FinderFactory $finders): array
{
if (! $this->shouldDiscoverEvents()) {
return [];
}
return $finders
->listenerDirectoryFinder()
->withModuleInfo()
->reduce(fn(array $discovered, ModuleFileInfo $file) => array_merge_recursive(
$discovered,
DiscoverEvents::within($file->getPathname(), $file->module()->path('src'))
), []);
}
3. Registration Phase
The handle() method registers discovered listeners:
public function handle(Collection $data): void
{
$data->each(function(array $listeners, string $event) {
foreach (array_unique($listeners, SORT_REGULAR) as $listener) {
$this->events->listen($event, $listener);
}
});
}
Duplicate listeners are automatically removed to prevent the same listener from being registered multiple times for the same event.
Configuration
You can control event discovery through the config/app-modules.php file:
return [
'should_discover_events' => true, // or false to disable
];
If not set, the plugin will use your application’s EventServiceProvider configuration.
Expected Module Structure
For listeners to be discovered, they must be located in:
app-modules/
└── your-module/
└── src/
└── Listeners/
└── SendWelcomeEmail.php
Example Listener
Event Class
namespace YourModule\Events;
class UserRegistered
{
public function __construct(
public User $user
) {}
}
Listener Class
namespace YourModule\Listeners;
use YourModule\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// Send welcome email to $event->user
}
}
Type Hinting
Laravel’s event discovery works by inspecting the type hints on your listener’s handle() method. The plugin uses the same DiscoverEvents utility that Laravel uses internally.
public function handle(UserRegistered $event): void
{
// The type hint tells Laravel this listener handles UserRegistered events
}
Queued Listeners
Listeners can implement ShouldQueue to be dispatched to the queue:
use Illuminate\Contracts\Queue\ShouldQueue;
class SendWelcomeEmail implements ShouldQueue
{
public function handle(UserRegistered $event): void
{
// This will run on the queue
}
}
Dependencies
Illuminate\Contracts\Events\Dispatcher - Event dispatcher
Illuminate\Contracts\Foundation\Application - Application instance
Illuminate\Contracts\Config\Repository - Configuration
InterNACHI\Modular\Support\DiscoverEvents - Event discovery utility