zaber-dev/laravel-context
A lightweight Laravel package for managing shared application context. Store and retrieve contextual data such as request metadata, tenant details, or user settings across middleware, services, and views without passing it manually.
Installation and first steps
composer require zaber-dev/laravel-context
If the package ships a service provider, confirm it is auto-discovered (php artisan about or php artisan package:discover). Check whether a config file is publishable (php artisan vendor:publish --tag=laravel-context)—look for knobs controlling which boundaries (queue, cache, storage, logging, events, routing, validation) auto-propagate.
Where to look first
README "What is Context" section—confirm the accessor façade name (e.g. Context::put()/get()/push()/pop()) so you match the package's API rather than Laravel's built-in Illuminate\Support\Facades\Context.Context facade—decide early which one is authoritative in your app to avoid two parallel context stores.First use case
The natural first win is a request correlation ID that survives a queued job:
// App\Http\Middleware\SeedContext.php
public function handle($request, $next)
{
Context::put('request_id', (string) Str::uuid());
return $next($request);
}
Because the package propagates across queues, any job dispatched during that request should see Context::get('request_id') without manual pass-through—verify this end-to-end with a single throwaway job before building anything else on it.
Middleware as the seeding layer
Seed context at the HTTP boundary (request ID, tenant ID, user ID, locale). The package propagates it outward; do not scatter put() calls throughout controllers—one middleware keeps the origin honest and testable.
Queue jobs
The package advertises cross-queue propagation. The realistic pattern is: dispatch a job → the worker restores the parent context → the job reads Context::get('tenant_id') and scopes Eloquent queries to it. This eliminates the "I forgot to pass $tenantId into the job constructor" class of bugs. If you already pass IDs explicitly, you can keep doing so defensively while relying on context for logging and cross-cutting concerns.
Cache and storage namespacing
For cache, the useful pattern is prefixing keys with a context-derived namespace (tenant or request scope) so a shared Redis/array store cannot leak entries across boundaries. For storage, context-aware paths (tenants/{tenant_id}/...) let you treat local/S3 disks uniformly. Confirm whether the package rewrites keys automatically or exposes helpers—if automatic, test the raw key shape that lands in your store so you can invalidate deliberately.
Logging
Attach the whole context to the logger (Monolog processor or Laravel's Log::withContext). This is often the highest-value integration: every log line in a request and its downstream jobs carries the correlation ID, so a single grep reconstructs the trace. Beware log size if you push large objects into context here.
Events, routing, validation
Event propagation means listeners fired from a queued job still see context (useful for auditing). Routing typically means route parameters or URLs resolving with a context base. Validation integration is usually about context-aware rules (e.g. tenant-scoped exists rules)—the pattern is a custom rule that reads from Context rather than passing the tenant into every Rule::exists().
Push/pop for nested scopes
Use Context::push() / pop() (or scope() if provided) around subtasks—e.g. a console command branching into per-customer loops—so context reverts cleanly and cannot bleed into the next iteration. Wrap scoping in try/finally.
Queue workers are long-lived
The classic failure mode: context set in job A leaks into job B on the same worker. Even if the package clears context between jobs, do not assume it—write a test that dispatches two jobs with different context synchronously (Bus::fake-style or queue:work --once twice) and assert the second doesn't see the first's keys. If leaking, subscribe to JobProcessing/JobProcessed events and Context::flush() defensively.
Octane / RoadRunner
Under Swoole, FrankenPHP, or RoadRunner, the request boundary is not the process boundary. Any context store that lives in a singleton risks cross-request leakage. Verify the package resets context in its Octane listeners (or add your own RequestTerminated/RequestReceived reset) and test with two concurrent-ish requests hitting different tenants.
Serialization limits
Context crosses queues by serialization. Only put scalar, JSON-safe values in context. Closures, Eloquent models with open connections, and resource handles will either throw or silently disappear. Store an ID and re-fetch in the consumer.
Two Context facades
If you are on Laravel 11+, you likely have Illuminate\Support\Facades\Context and this package's facade. Pick one. Importing the wrong Context is a silent bug—the call succeeds but the value lands in the other store. Add a coding-standard rule (or a PHPStan alias) if your team is prone to this.
Log noise and PII
Ambient context is the easiest place for secrets to end up in logs. Be explicit about which keys get attached to the logger, and never put tokens, passwords, or full request bodies into context. Prefer a whitelist of loggable context keys.
Cache key surprises
If key rewriting is automatic, Cache::forget($key) in an ops script will miss because the actual key is scoped. Either use the package's forget helper or document the composed key format. Check whether cache:clear respects context scoping (it probably shouldn't—clearing should be global).
Debugging
When propagation appears broken, bisect the boundary: log the full context at the sender, at the worker boot, and inside the job handle. If it's present at boot but gone in handle, propagation is fine but something is clearing it (usually a middleware or job middleware). If absent at boot, the job was dispatched with an already-empty context—look upstream at the dispatching request.
Config quirks
Auto-propagation is convenient but occasionally wrong (e.g. you don't want context in a queue you share with a third-party system). Disable individual adapters in config rather than fighting them at call sites. Conversely, propagation that is off by default for validation or routing is easy to miss—read the config table once and write down which boundaries are active in your app.
Extension points
Most packages of this shape allow registering custom propagators/adapters. If you have an internal boundary (a webhook dispatch, a gRPC client, a scheduled command), a custom adapter is cleaner than manual put() calls. Look for a documented contract (e.g. PropagatesContext interface or a registry method) before reaching for middleware hacks.
How can I help you explore Laravel packages today?