jooservices/laravel-events
Laravel package for adding event management to your app, with helpers for creating, scheduling, and displaying events. Lightweight and easy to integrate, it offers a simple foundation for event listings, calendars, and related workflows.
This package persists Laravel domain events to MongoDB and exposes a queryable event log. For v4, start with UPGRADE-4.0.md even on a fresh install — it encodes the breaking changes that affect call sites, test doubles, indexes, and dependency injection.
Minimal setup:
composer require jooservices/laravel-events:^4.0
php artisan vendor:publish --tag=events-config # only if you customize config
php artisan events:install-indexes
Then verify MongoDB connectivity and that the new index set is present: sparse unique event_id, event_name, and top-level correlation/causation fields. The service provider remains open, so normal Laravel package discovery applies.
First practical use case: record a domain event and query it back.
use Jooservices\LaravelEvents\Enums\EventLogAction;
event(new OrderShipped($order));
$events = $eventQuery->byEventName('order.shipped')
->between($from, $to)
->get();
Before writing application code, inspect:
config/events.php for persistence, query, serializer, and context_provider configuration.EventPersisterInterface — the preferred dependency for subscribers and tests.EventService constructor for optional PSR-20 ClockInterface and PSR-3 LoggerInterface.EventLogAction and EventCategory; use ->value when a string is required.Persist through the interface, not the concrete service. EventService is final in v4. Type-hint EventPersisterInterface in subscribers, jobs, and tests. This is the intended DIP seam and avoids mocking final classes.
final class PersistOrderEvent
{
public function __construct(private EventPersisterInterface $persister) {}
public function handle(OrderShipped $event): void
{
$this->persister->persist($event);
}
}
Use ShouldDispatchAfterCommit for transactional domain events. Events that must not outlive a rolled-back SQL transaction should implement the marker interface. For queued events, embed user_id and correlation identifiers directly in the event payload rather than relying on ambient request state.
Subscribers should use the persister interface and enum values. When comparing actions or categories, use EventLogAction::UPDATED->value where a string is expected. EventLogAction::all() still returns list<string>, so it is suitable for allowlists and validation rules.
Query through the provided query services. Use byEventName() for event-name filters, byEventClass() for class filters, and byEventId() for identity lookups. Correlation queries support dual paths in v4, so prefer the package methods over hand-built MongoDB filters. between() is guarded and rejects inverted ranges via InvalidQueryException; date strings from Eloquent/Mongo are hydrated by the query DTO layer.
$events = $query->byEventClass(OrderShipped::class)
->byEventId($eventId)
->between($from, $to)
->get();
Bulk writes should go through recordManyStoredEvents(). It always runs ensureEnvelope() in v4, so envelopes stay consistent for batch ingestion. If you implement a custom serializer, EventSerializerInterface now requires ensureEnvelope().
Use an invokable context_provider class-string for request/user context. Non-callable providers now throw InvalidConfigurationException instead of failing silently. Keep the provider deterministic and test it directly.
Guest events must not clobber context. v4 fixes guest EventLog writes overwriting context user_id with null. If you relied on the old behavior, adjust expectations; otherwise leave user context resolution to the provider.
Inject clock and logger for deterministic behavior. Pass a PSR-20 clock when timestamps matter in tests or audits, and a PSR-3 logger for diagnostics around persistence failures.
Prefer composition over subclassing. Query services, subscribers, serializer, DiffHelper, and the console command are final. Extend behavior by implementing interfaces or decorating package services in your own classes.
In tests, bind the interface. Mock or fake EventPersisterInterface rather than EventService. For Mongo-backed integration tests, the Dockerfile/docker-compose/Makefile provide a PHP 8.5 + MongoDB environment aligned with CI.
EventLogAction::UPDATED string comparisons/returns with EventLogAction::UPDATED->value. Search for enum cases used in array keys, JSON, validation rules, and database filters.php artisan events:install-indexes is mandatory; skipping it can cause duplicate event_id rows or slow correlation queries.mock(EventService::class) or extend subscribers/query services, switch to EventPersisterInterface or composition. Eloquent models and EventsServiceProvider remain open.EventDiffData and EventMetadataData no longer exist. Record DTOs expose diff, meta, and metadata as arrays; update type hints and property access.EventSerializerInterface implementation must add ensureEnvelope(). Missing it is a hard fatal, not a soft fallback.context_provider throws InvalidConfigurationException. Clear config cache after publishing or editing config, especially in CI.InvalidQueryException. Inverted between() ranges are rejected rather than silently normalized.EventLog entries with empty changed now produce a full removal diff. Snapshot tests around diffs should be refreshed.user_id with null. Audit assumptions in audit-log or activity-feed consumers.jooservices/dto ^3.2, jooservices/exceptions ^4.0, psr/clock ^1.0, and psr/log ^3.0 must be compatible with your app. Watch for PSR-3 v3 conflicts in packages pinned to v1/v2.EventService, watch for InvalidEventDataException, InvalidQueryException, and InvalidConfigurationException, then verify indexes and config cache. For correlation issues, confirm top-level correlation/causation fields exist on newly written documents.EventPersisterInterface, EventSerializerInterface, or an invokable context provider; use the open Eloquent models and EventsServiceProvider for application-level wiring. The final query services and EventService are intentionally closed — decorate rather than inherit.max with strict rules, Pint PER-CS 3.0, and Conventional Commit subject checks are part of the package baseline. Adopt the same locally to avoid PR gate surprises when contributing or integrating.How can I help you explore Laravel packages today?