2026-03-25 18:22:09 +00:00
|
|
|
# Job Entrypoint (`dispatch`, `asJob`)
|
|
|
|
|
|
|
|
|
|
## Scope
|
|
|
|
|
|
|
|
|
|
Use this reference when running an action through queues.
|
|
|
|
|
|
|
|
|
|
## Recap
|
|
|
|
|
|
|
|
|
|
- Lists async/sync dispatch helpers and conditional dispatch variants.
|
|
|
|
|
- Covers job wrapping/chaining with `makeJob`, `makeUniqueJob`, and `withChain`.
|
|
|
|
|
- Documents queue assertion helpers for tests (`assertPushed*`).
|
|
|
|
|
- Summarizes `JobDecorator` hooks/properties for retries, uniqueness, timeout, and failure handling.
|
|
|
|
|
|
|
|
|
|
## Recommended pattern
|
|
|
|
|
|
|
|
|
|
- Dispatch with `Action::dispatch(...)` for async execution.
|
|
|
|
|
- Keep queue-specific orchestration in `asJob(...)`.
|
|
|
|
|
- Keep reusable business logic in `handle(...)`.
|
|
|
|
|
|
|
|
|
|
## Methods provided (`AsJob` trait)
|
|
|
|
|
|
|
|
|
|
### `dispatch`
|
|
|
|
|
|
|
|
|
|
Dispatches the action asynchronously.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatch($team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `dispatchIf`
|
|
|
|
|
|
|
|
|
|
Dispatches asynchronously only if condition is met.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatchIf($team->plan === 'premium', $team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `dispatchUnless`
|
|
|
|
|
|
|
|
|
|
Dispatches asynchronously unless condition is met.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatchUnless($team->plan === 'free', $team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `dispatchSync`
|
|
|
|
|
|
|
|
|
|
Dispatches synchronously.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatchSync($team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `dispatchNow`
|
|
|
|
|
|
|
|
|
|
Alias of `dispatchSync`.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatchNow($team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `dispatchAfterResponse`
|
|
|
|
|
|
|
|
|
|
Dispatches synchronously after the HTTP response is sent.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
SendTeamReportEmail::dispatchAfterResponse($team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `makeJob`
|
|
|
|
|
|
|
|
|
|
Creates a `JobDecorator` wrapper. Useful with `dispatch(...)` helper or chains.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
dispatch(SendTeamReportEmail::makeJob($team));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `makeUniqueJob`
|
|
|
|
|
|
|
|
|
|
Creates a `UniqueJobDecorator` wrapper. Usually automatic with `ShouldBeUnique`, but can be forced.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
dispatch(SendTeamReportEmail::makeUniqueJob($team));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `withChain`
|
|
|
|
|
|
|
|
|
|
Attaches jobs to run after successful processing.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
$chain = [
|
|
|
|
|
OptimizeTeamReport::makeJob($team),
|
|
|
|
|
SendTeamReportEmail::makeJob($team),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
CreateNewTeamReport::withChain($chain)->dispatch($team);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Equivalent using `Bus::chain(...)`:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Illuminate\Support\Facades\Bus;
|
|
|
|
|
|
|
|
|
|
Bus::chain([
|
|
|
|
|
CreateNewTeamReport::makeJob($team),
|
|
|
|
|
OptimizeTeamReport::makeJob($team),
|
|
|
|
|
SendTeamReportEmail::makeJob($team),
|
|
|
|
|
])->dispatch();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Chain assertion example:
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Illuminate\Support\Facades\Bus;
|
|
|
|
|
|
|
|
|
|
Bus::fake();
|
|
|
|
|
|
|
|
|
|
Bus::assertChained([
|
|
|
|
|
CreateNewTeamReport::makeJob($team),
|
|
|
|
|
OptimizeTeamReport::makeJob($team),
|
|
|
|
|
SendTeamReportEmail::makeJob($team),
|
|
|
|
|
]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `assertPushed`
|
|
|
|
|
|
|
|
|
|
Asserts the action was queued.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
|
|
|
|
|
|
Queue::fake();
|
|
|
|
|
|
|
|
|
|
SendTeamReportEmail::assertPushed();
|
|
|
|
|
SendTeamReportEmail::assertPushed(3);
|
|
|
|
|
SendTeamReportEmail::assertPushed($callback);
|
|
|
|
|
SendTeamReportEmail::assertPushed(3, $callback);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`$callback` receives:
|
|
|
|
|
- Action instance.
|
|
|
|
|
- Dispatched arguments.
|
|
|
|
|
- `JobDecorator` instance.
|
|
|
|
|
- Queue name.
|
|
|
|
|
|
|
|
|
|
### `assertNotPushed`
|
|
|
|
|
|
|
|
|
|
Asserts the action was not queued.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
|
|
|
|
|
|
Queue::fake();
|
|
|
|
|
|
|
|
|
|
SendTeamReportEmail::assertNotPushed();
|
|
|
|
|
SendTeamReportEmail::assertNotPushed($callback);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `assertPushedOn`
|
|
|
|
|
|
|
|
|
|
Asserts the action was queued on a specific queue.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Illuminate\Support\Facades\Queue;
|
|
|
|
|
|
|
|
|
|
Queue::fake();
|
|
|
|
|
|
|
|
|
|
SendTeamReportEmail::assertPushedOn('reports');
|
|
|
|
|
SendTeamReportEmail::assertPushedOn('reports', 3);
|
|
|
|
|
SendTeamReportEmail::assertPushedOn('reports', $callback);
|
|
|
|
|
SendTeamReportEmail::assertPushedOn('reports', 3, $callback);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Methods used (`JobDecorator`)
|
|
|
|
|
|
|
|
|
|
### `asJob`
|
|
|
|
|
|
|
|
|
|
Called when dispatched as a job. Falls back to `handle(...)` if missing.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
class SendTeamReportEmail
|
|
|
|
|
{
|
|
|
|
|
use AsAction;
|
|
|
|
|
|
|
|
|
|
public function handle(Team $team, bool $fullReport = false): void
|
|
|
|
|
{
|
|
|
|
|
// Prepare report and send it to all $team->users.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function asJob(Team $team): void
|
|
|
|
|
{
|
|
|
|
|
$this->handle($team, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobMiddleware`
|
|
|
|
|
|
|
|
|
|
Adds middleware to the queued action.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobMiddleware(array $parameters): array
|
|
|
|
|
{
|
|
|
|
|
return [new RateLimited('reports')];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `configureJob`
|
|
|
|
|
|
|
|
|
|
Configures `JobDecorator` options.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
use Lorisleiva\Actions\Decorators\JobDecorator;
|
|
|
|
|
|
|
|
|
|
public function configureJob(JobDecorator $job): void
|
|
|
|
|
{
|
|
|
|
|
$job->onConnection('my_connection')
|
|
|
|
|
->onQueue('my_queue')
|
|
|
|
|
->through(['my_middleware'])
|
|
|
|
|
->chain(['my_chain'])
|
|
|
|
|
->delay(60);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobConnection`
|
|
|
|
|
|
|
|
|
|
Defines queue connection.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public string $jobConnection = 'my_connection';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobQueue`
|
|
|
|
|
|
|
|
|
|
Defines queue name.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public string $jobQueue = 'my_queue';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobTries`
|
|
|
|
|
|
|
|
|
|
Defines max attempts.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobTries = 10;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobMaxExceptions`
|
|
|
|
|
|
|
|
|
|
Defines max unhandled exceptions before failure.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobMaxExceptions = 3;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobBackoff`
|
|
|
|
|
|
|
|
|
|
Defines retry delay seconds.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobBackoff = 60;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobBackoff`
|
|
|
|
|
|
|
|
|
|
Defines retry delay (int or per-attempt array).
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobBackoff(): int
|
|
|
|
|
{
|
|
|
|
|
return 60;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getJobBackoff(): array
|
|
|
|
|
{
|
|
|
|
|
return [30, 60, 120];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobTimeout`
|
|
|
|
|
|
|
|
|
|
Defines timeout in seconds.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobTimeout = 60 * 30;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobRetryUntil`
|
|
|
|
|
|
|
|
|
|
Defines timestamp retry deadline.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobRetryUntil = 1610191764;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobRetryUntil`
|
|
|
|
|
|
|
|
|
|
Defines retry deadline as `DateTime`.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobRetryUntil(): DateTime
|
|
|
|
|
{
|
|
|
|
|
return now()->addMinutes(30);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobDisplayName`
|
|
|
|
|
|
|
|
|
|
Customizes queued job display name.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobDisplayName(): string
|
|
|
|
|
{
|
|
|
|
|
return 'Send team report email';
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobTags`
|
|
|
|
|
|
|
|
|
|
Adds queue tags.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobTags(Team $team): array
|
|
|
|
|
{
|
|
|
|
|
return ['report', 'team:'.$team->id];
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobUniqueId`
|
|
|
|
|
|
|
|
|
|
Defines uniqueness key when using `ShouldBeUnique`.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobUniqueId(Team $team): int
|
|
|
|
|
{
|
|
|
|
|
return $team->id;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobUniqueId`
|
|
|
|
|
|
|
|
|
|
Static uniqueness key alternative.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public string $jobUniqueId = 'some_static_key';
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobUniqueFor`
|
|
|
|
|
|
|
|
|
|
Defines uniqueness lock duration in seconds.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobUniqueFor(Team $team): int
|
|
|
|
|
{
|
|
|
|
|
return $team->role === 'premium' ? 1800 : 3600;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobUniqueFor`
|
|
|
|
|
|
|
|
|
|
Property alternative for uniqueness lock duration.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public int $jobUniqueFor = 3600;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobUniqueVia`
|
|
|
|
|
|
|
|
|
|
Defines cache driver used for uniqueness lock.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobUniqueVia()
|
|
|
|
|
{
|
|
|
|
|
return Cache::driver('redis');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `$jobDeleteWhenMissingModels`
|
|
|
|
|
|
|
|
|
|
Property alternative for missing model handling.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public bool $jobDeleteWhenMissingModels = true;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `getJobDeleteWhenMissingModels`
|
|
|
|
|
|
|
|
|
|
Defines whether jobs with missing models are deleted.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function getJobDeleteWhenMissingModels(): bool
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### `jobFailed`
|
|
|
|
|
|
|
|
|
|
Handles job failure. Receives exception and dispatched parameters.
|
|
|
|
|
|
|
|
|
|
```php
|
|
|
|
|
public function jobFailed(?Throwable $e, ...$parameters): void
|
|
|
|
|
{
|
|
|
|
|
// Notify users, report errors, trigger compensations...
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Checklist
|
|
|
|
|
|
|
|
|
|
- Async/sync dispatch method matches use-case (`dispatch`, `dispatchSync`, `dispatchAfterResponse`).
|
|
|
|
|
- Queue config is explicit when needed (`$jobConnection`, `$jobQueue`, `configureJob`).
|
|
|
|
|
- Retry/backoff/timeout policies are intentional.
|
|
|
|
|
- `asJob(...)` delegates to `handle(...)` unless queue-specific branching is required.
|
|
|
|
|
- Queue tests use `Queue::fake()` and action assertions (`assertPushed*`).
|
|
|
|
|
|
|
|
|
|
## Common pitfalls
|
|
|
|
|
|
|
|
|
|
- Embedding domain logic only in `asJob(...)`.
|
|
|
|
|
- Forgetting uniqueness/timeout/retry controls on heavy jobs.
|
|
|
|
|
- Missing queue-specific assertions in tests.
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
2026-06-11 09:58:42 +00:00
|
|
|
- https://www.laravelactions.com/2.x/as-job.html
|