A Laravel queue infrastructure package that preserves application execution context (tenant, school, user, module) when dispatching and processing queued jobs. Designed for multi-tenant and multi-school Laravel applications.
In multi-tenant or multi-school applications, queued jobs often need access to the same context (tenant, school, user, module) that was present when the job was originally dispatched. Without context propagation, each job would need to manually resolve this information, leading to duplicated code and potential inconsistencies.
This package provides a clean, extensible way to automatically capture and restore the application execution context when dispatching and processing queued jobs.
composer require schoolpalm/queued-jobsphp artisan vendor:publish --tag=queued-jobs-configphp artisan migrateExtend ContextAwareJob:
<?php
namespace App\Jobs;
use SchoolPalm\QueuedJobs\Jobs\ContextAwareJob;
class GenerateReport extends ContextAwareJob
{
public function __construct(
private readonly array $reportData
) {}
public function handle(): void
{
// Context is automatically available
$context = $this->getQueueContext();
$schoolId = $context['school_id'];
$userId = $context['user_id'];
$module = $context['module'];
// ... your job logic
}
}In your AppServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use SchoolPalm\QueuedJobs\Facades\QueuedJobs;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
QueuedJobs::resolveContextUsing(function () {
return [
'tenant_id' => tenancy()->tenant?->id,
'school_id' => session('school_id'),
'user_id' => auth()->id(),
'module' => request()->route('module'),
'metadata' => [
'ip' => request()->ip(),
],
];
});
}
}The restorer runs before each queued job executes, restoring the application state:
QueuedJobs::restoreContextUsing(function (QueueContext $context) {
// Switch tenant database
if ($context->tenantId()) {
tenancy()->initialize($context->tenantId());
}
// Set current school
if ($context->schoolId()) {
session(['school_id' => $context->schoolId()]);
}
// Authenticate user
if ($context->userId()) {
auth()->loginUsingId($context->userId());
}
});use SchoolPalm\QueuedJobs\Facades\QueuedJobs;
// Basic dispatch — context is captured automatically
QueuedJobs::job(new GenerateReport($data))->dispatch();
// With fluent context overrides
QueuedJobs::job(new GenerateReport($data))
->withTenant(1)
->withSchool(20)
->withUser(50)
->withModule('reports')
->withMetadata(['source' => 'api'])
->onQueue('high')
->onConnection('redis')
->delay(now()->addMinutes(5))
->dispatch();If the job is dispatched via GenerateReport::dispatch(), the context will NOT be attached automatically. Always use the QueuedJobs::job() facade to ensure context propagation.
Start building a job dispatch.
| Method | Description |
|---|---|
->withTenant(string|int $id) |
Attach tenant context |
->withSchool(string|int $id) |
Attach school context |
->withUser(string|int $id) |
Attach user context |
->withModule(string $module) |
Attach module context |
->withMetadata(array $data) |
Attach arbitrary metadata |
->withContext(array|QueueContext $ctx) |
Attach full context object or array |
->onConnection(?string $conn) |
Set queue connection |
->onQueue(?string $queue) |
Set queue name |
->delay($delay) |
Set job delay |
->dispatch() |
Execute the dispatch |
Register a callback that captures the current application context.
Register a callback that restores application context before job execution.
Set global or default context.
The package includes optional job result tracking with a database-backed results table.
use SchoolPalm\QueuedJobs\Managers\JobResultManager;
$manager = app(JobResultManager::class);
$result = $manager->create($job, [
'school_id' => 10,
'user_id' => 5,
]);// Inside your job's handle() method
$this->completeResult(['file' => 'report.pdf', 'pages' => 10]);
$this->failResult('Processing failed: memory limit exceeded');use SchoolPalm\QueuedJobs\Facades\QueuedJobs;
// Get results builder
$jobs = QueuedJobs::jobs();
// Filtering
$jobs->forSchool(10)
->forUser(5)
->forModule('reports')
->completed()
->latest();
// Execute
$results = $jobs->get();
$result = $jobs->first();
$paginated = $jobs->paginate(20);
$count = $jobs->count();
$exists = $jobs->exists();Context is serialized with the job into the Laravel queue payload. The QueueContext value object is immutable and safely serializes/unserializes via PHP's native serialization. When the queue worker pops the job:
- Laravel unserializes the job (including the
queueContextarray) - The
RestoreJobContextmiddleware fires - The array context is converted back to a
QueueContextvalue object - Your registered
restoreContextUsingcallback is invoked - Your job's
handle()method runs with full context restored
src/
├── Builders/
│ ├── JobBuilder.php # Fluent builder for job dispatch
│ └── JobResultBuilder.php # Query builder for job results
├── Context/
│ └── QueueContext.php # Immutable context value object
├── Enums/
│ └── JobResultStatus.php # Status enum (pending/processing/completed/failed)
├── Facades/
│ └── QueuedJobs.php # Facade for QueuedJobsManager
├── Jobs/
│ └── ContextAwareJob.php # Base job class with context support
├── Managers/
│ ├── QueuedJobsManager.php # Core manager (context capture/restore)
│ └── JobResultManager.php # CRUD for job results
├── Middleware/
│ └── RestoreJobContext.php # Queue middleware for context restoration
├── Models/
│ └── QueueJobResult.php # Eloquent model for job results
├── Providers/
│ └── QueuedJobsServiceProvider.php
├── Resources/
│ └── JobResultResource.php # API resource transformer
└── Support/
├── PendingJob.php # Dispatch wrapper (avoids PendingDispatch)
└── QueueConfiguration.php # Config accessor
| Option | Default | Description |
|---|---|---|
connection |
QUEUE_CONNECTION env |
Default queue connection |
queue |
QUEUE_NAME env |
Default queue name |
capture_context |
true |
Auto-capture context on dispatch |
auto_restore_context |
true |
Auto-restore context on job execution |
middleware |
[RestoreJobContext::class] |
Middleware applied to jobs |
tries |
3 |
Default job retry count |
timeout |
120 |
Default job timeout (seconds) |
composer testPlease see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security-related issues, please email dev@schoolpalm.com instead of using the issue tracker.
The MIT License (MIT). Please see License File for more information.