Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Event Source Laravel Package

dessimoney/event-source

Event sourcing for Laravel and PHP applications: persist domain events, rebuild aggregates from history, and maintain projections. The package provides an event store, aggregate support, and Laravel-friendly tooling for building event-driven applications with event-sourced persistence.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require dessimoney/event-source. The core is framework-agnostic; the Laravel bridge is a thin service provider/facade on top of it.
  • In Laravel, package discovery should register the provider automatically. If not, add the provider from the package's src/Laravel/ (or src/Bridge/Laravel/) namespace to config/app.php, then publish config with php artisan vendor:publish --provider="Dessimoney\EventSource\Laravel\EventSourceServiceProvider" (check the README for the exact tag).
  • Look first at: README.md for the minimal example, src/ for the EventSource/Event core, and the Laravel bridge for the facade, route macro, and config keys. The API surface is small, so ten minutes in src/ tells you everything the docs don't.
  • Check composer.json for supported Laravel/PHP versions. The last release is recent, but with 3 stars and 0 dependents, pin an exact version.
  • First use case: a heartbeat endpoint. Register a GET route, open it with curl -N http://app.test/events, and confirm you see event:/data: frames arrive incrementally rather than as one buffered blob.
// routes/web.php — confirm facade/method names against the README first
use Dessimoney\EventSource\Laravel\Facades\EventSource;

Route::get('/events', function () {
    return EventSource::response(function ($stream) {
        $stream->event('hello', ['user' => auth()->id()]);

        while (!connection_aborted()) {
            $stream->comment('keep-alive');
            sleep(15);
        }
    });
})->withoutMiddleware([\Barryvdh\Debugbar\Middleware\InjectDebugbar::class]);

Open it from the browser with new EventSource('/events') and add a listener for hello.

Implementation Patterns

  • One worker per connection. An SSE request occupies a PHP-FPM worker/process for its whole lifetime. Treat that as a hard capacity limit and keep loop work cheap: read from cache/Redis, don't run queries in a tight cycle.
  • Progress reporting. Queue a job, have it write progress:{jobId} to the cache or a Redis hash, and let the SSE endpoint poll every 250–500 ms, emit a frame, and exit when status is done or failed. This is the lowest-friction fit for Laravel and avoids a pub/sub dependency.
  • Fan-out from Laravel events. In-process listeners won't reach a separate SSE worker. Use Redis pub/sub (a blocking Redis::blpop("sse:{$channel}", 20) loop is usually enough) or a database outbox table polled by id > $lastSeenId.
  • Keep the route dumb. Extract "what to emit next" into a service class with a next(): ?Event method. The route/controller just loops and writes. This makes the interesting logic unit-testable without fighting streamed responses.
  • Auth. EventSource cannot send custom headers, so use session cookies (same-origin) or URL::temporarySignedRoute() for cross-origin/third-party consumers. Exclude stream routes from aggressive global throttles, or reconnects will 429; use a dedicated limiter instead.
  • Protocol fields. Use id: so clients send Last-Event-ID on reconnect (implement a small replay buffer if you care about gap-free delivery), retry: to control client backoff, and event: so the front end can use addEventListener('progress', ...) instead of parsing everything in onmessage.
  • Plain PHP/Symfony. The same core returns a StreamedResponse/PSR-7 stream; the Laravel facade is not doing anything magic. If you later drop the package, response()->stream() with the same headers is a viable fallback.

Gotchas and Tips

  • Buffering is the #1 failure mode. Nginx (fastcgi_buffering, proxy_buffering) and Apache will hold frames until the response ends. Send X-Accel-Buffering: no plus Content-Type: text/event-stream, Cache-Control: no-cache. Also disable PHP output_buffering and zlib.output_compression for the route, and turn off Debugbar/Telescope middleware — they buffer output.
  • Session locking. Laravel holds the session file lock for the request. Call session_write_close() before entering the loop, otherwise every other request from that user blocks until the stream ends.
  • Disconnects. Set ignore_user_abort(true) and check connection_aborted(); PHP often only notices a closed socket on the next write. A : keep-alive comment every 15–30 s doubles as proxy idle-timeout protection.
  • No output before the response. A stray BOM, whitespace after ?>, or an earlier echo/dd() corrupts the stream. Nothing may be printed before the streamed response is returned.
  • HTTP/1.1 connection cap. Browsers allow ~6 connections per origin; several tabs on one SSE endpoint can starve your API calls. Serve over HTTP/2 or share a single stream across tabs.
  • Memory in long loops. Don't accumulate arrays, and periodically DB::reconnect() (or equivalent) in jobs that run for hours. Also set set_time_limit(0) for the stream route and watch request_terminate_timeout in PHP-FPM.
  • Testing. Streamed responses are lazy; assertSee frequently sees nothing. Test the feed/service class directly, and for HTTP use $response->streamedContent() with an iteration cap so the test terminates.
  • Config quirks. Config caching (php artisan config:cache) means published changes need a recache; check whether the package reads env vars directly rather than through config(). Verify default heartbeat/retry values match your proxy timeouts.
  • Version risk. Low adoption means the API can drift between minors. Pin the version and wrap the package behind a small adapter/facade so you can swap to a hand-rolled response()->stream() or Mercure without touching call sites. Extension seams are typically the service-provider bindings, the event formatter/serializer, and middleware.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib