bnnvara/event-catalog
A Laravel package that provides a catalog for your application's events, letting you register, organize, and look up domain and broadcast events in one place. Useful for documenting event flows, wiring listeners, and keeping event-driven code discoverable and consistent.
This is a Symfony bundle, not a Laravel package, so the first step is confirming you actually need it: it targets Symfony's EventDispatcher + bundle service-loading conventions. In a Laravel app you'll typically consume it via a thin bridge (register the bundle's services in a service provider) or not at all — Laravel's own event system diverges from Symfony's dispatch signature enough that direct reuse is fragile.
First moves:
composer.json + DependencyInjection/ — these tell you the supported Symfony versions, required PHP version, and which services/aliases get registered. Do this before composer require; the package is unmaintained (0 stars, 0 dependents), so pin a compatible Symfony range or vendor it.Event/, Events/, or Message/. "Catalog events" almost certainly means domain events emitted when catalog entities (media items, assets, shows) change state — ItemCreated, ItemUpdated, ItemPublished, etc. Enumerate them; this is your API surface.CompilerPass, services.yaml tagged listeners, or an EventSubscriberInterface implementation. This is where you hook in.Minimal bridge sketch (Laravel side):
// app/Providers/EventCatalogServiceProvider.php
public function register(): void
{
$this->app->singleton(EventCatalogContainer::class, function () {
$container = new ContainerBuilder();
$container->registerExtension(new EventCatalogExtension());
$container->loadFromExtension('event_catalog', config('event-catalog'));
(new EventCatalogBundle())->build($container);
$container->compile();
return $container;
});
}
Only go down this path if the bundle exposes plain classes you can instantiate directly — prefer that over booting a Symfony kernel inside Laravel.
Event-first persistence (outbox). The most reliable pattern with a catalog-event bundle: subscribe once, write the raw event to an outbox table in the same transaction as the catalog mutation, and have a queued job forward it downstream. This decouples you from dispatcher reliability and gives a replay path.
class PersistCatalogEvent
{
public function handle(object $event): void
{
OutboxEvent::create([
'type' => get_class($event),
'payload' => json_encode($event),
'occurred_at' => now(),
]);
}
}
Type-mapped fan-out. Enumerate the bundle's event classes in a single map (event class => [listeners]) so registration is declarative and greppable. A compiler pass can auto-tag listeners (kernel.event_subscriber) if the bundle supports it — reuse that rather than hand-wiring.
Integration tip — one adapter layer. Wrap the bundle's events behind your own domain events. When the package is abandoned or replaced, only the adapter changes, not every consumer. This is the single highest-leverage decision when depending on an unmaintained bundle.
Idempotency by event ID. Catalog systems emit near-duplicates (update-on-save, retried dispatches). Persist a stable event identifier if the bundle provides one; otherwise derive a hash of class + aggregateId + version + payload. Enforce a unique index.
Testing. The bundle's services are registered in a container; in tests, build a fresh container per test rather than reusing a compiled one. Assert on the event objects directly — they should be plain POPOs with getters, making assertions trivial compared to mocking the dispatcher.
composer require will install it, but nothing boots. You must manually register services or bypass the bundle and use its classes directly. Many Symfony bundles assume kernel.event_dispatcher, doctrine, or logger services exist — those IDs differ or are absent in Laravel.Symfony\Component\* APIs, and container->get() calls before trusting it in a request path. Consider vendoring (fork into app/Support) so you can patch.EventDispatcherInterface::dispatch($event, $eventName) argument order was reversed in Symfony 4.4 (dispatch($event, string $eventName = null)). If this bundle predates that, event names won't resolve and listeners silently never fire. Verify the Symfony version constraint against your Laravel version's Symfony components.$dispatcher->getListeners($eventName) after boot — if empty, your registration is the bug, not the event.Configuration.php defines the config tree; loadFromExtension('event_catalog', [...]) will throw on unknown keys. Map your config/event-catalog.php exactly, and default optional branches rather than passing null.doctrine-tagged listener, a custom EventSubscriberInterface you can decorate, or a CompilerPass that scans for tags. If none exist, the only clean extension point is wrapping the dispatcher — subclass or decorate EventDispatcherInterface and register it as the bundle's dispatcher service.bin/console debug:event-dispatcher (Symfony) is the fastest way to see what's actually registered. In a Laravel bridge, replicate the equivalent by iterating the compiled container's tagged services.app/Domain/Events and drop the bundle. You lose the registration sugar and gain full control — usually the right call for a 0-star dependency.How can I help you explore Laravel packages today?