Queue Configuration
Executables support all three of Laravel's queue configuration methods (class-level defaults, named methods, and dispatch-time options) and add a fourth: the configure() hook. These four layers stack in priority order (lowest to highest).
Layer 1: Class-Level Defaults
All Laravel job properties, interfaces, and attributes work directly on executables. The package reads them at build time:
class ProcessOrder
{
use QueueableExecutable;
public int $tries = 5;
public int $timeout = 120;
public string $queue = "orders";
public int $backoff = 10;
public string $connection = "redis";
public int $maxExceptions = 3;
public bool $failOnTimeout = true;
public bool $deleteWhenMissingModels = true;
public function execute(Order $order): void
{
/* ... */
}
}#[WithoutRelations]
#[DeleteWhenMissingModels]
class ProcessOrder implements ShouldBeEncrypted, ShouldBeUnique, ShouldQueueAfterCommit
{
use QueueableExecutable;
public function execute(Order $order): void
{
/* ... */
}
}Interfaces and attributes override properties when they conflict.
Layer 2: The configure() Hook
This layer is unique to executables. A method that receives a QueueableConfig object as its first parameter, followed by execute arguments by name. It gives you a single place to define all queue configuration dynamically, based on the actual input to execute(). All methods on QueueableConfig are chainable:
class ProcessOrder
{
use QueueableExecutable;
public function configure(QueueableConfig $config, int $amount, string $currency): void
{
$config
->tries($amount > 1000 ? 5 : 3)
->onQueue($currency === "USD" ? "payments-us" : "payments-intl")
->onConnection("redis")
->timeout(120)
->backoff([30, 60, 120])
->maxExceptions(3)
->failOnTimeout()
->deleteWhenMissingModels()
->delay(now()->addMinutes(5))
->shouldBeEncrypted()
->withoutRelations()
->displayName("order-{$amount}-{$currency}")
->afterCommit();
}
public function execute(string $orderId, int $amount, string $currency): void
{
// ...
}
}The first parameter must be QueueableConfig. After that, configure() only needs to declare the execute-parameters it cares about. The rest are skipped (see Argument Matching).
All QueueableConfig Methods
| Method | Purpose |
|---|---|
tries(int) | Max retry attempts |
timeout(int) | Job timeout in seconds |
backoff(int|array) | Backoff in seconds (or array for exponential) |
maxExceptions(int) | Max exceptions before stopping |
delay(array|int|DateTime|DateInterval) | Delay before first execution |
withoutDelay() | Remove any delay |
onQueue(string|UnitEnum) | Target queue name |
onConnection(string|UnitEnum) | Target connection |
allOnQueue(string|UnitEnum) | Queue for job + entire chain |
allOnConnection(string|UnitEnum) | Connection for job + entire chain |
chain(array) | Append a job chain |
afterCommit() | Dispatch after DB transaction commits |
beforeCommit() | Dispatch immediately (don't wait for commit) |
failOnTimeout() | Mark as failed on timeout (instead of retry) |
deleteWhenMissingModels() | Skip job if serialized model is deleted |
shouldBeEncrypted() | Encrypt job payload |
shouldBeUnique() | Lock until job completes |
shouldBeUniqueUntilProcessing() | Lock until processing starts |
uniqueId(string|int) | Custom unique lock identifier |
uniqueFor(int) | Unique lock duration in seconds |
displayName(string) | Custom display name for queue UI |
retryUntil(DateTime) | Stop retrying after this time |
withoutRelations() | Don't serialize model relations |
Layer 3: Named Methods
These are the same methods you'd define on a Laravel job. The difference: on executables, they also receive execute-arguments by name. Their return value overrides layers 1 and 2.
| Method | Returns | Purpose |
|---|---|---|
backoff() | int|array | Backoff in seconds (or array for exponential) |
displayName() | string | Custom name for queue UI / Horizon |
retryUntil() | DateTimeInterface | Stop retrying after this time |
tries() | int | Max retry attempts |
uniqueFor() | int | Unique lock duration in seconds |
uniqueId() | string|int | Unique lock identifier |
public function displayName(Order $order): string
{
return "Process order #{$order->reference}";
}
public function tries(Order $order): int
{
return $order->amount > 5000 ? 10 : 3;
}
public function backoff(): array
{
return [30, 60, 120]; // exponential backoff
}
public function retryUntil(): DateTime
{
return now()->addHours(6);
}
public function uniqueId(Order $order): string
{
return "process-{$order->id}";
}
public function uniqueFor(): int
{
return 300; // 5 minutes
}Layer 4: Dispatch-Time Fluent API
The caller overrides everything:
ProcessOrder::onQueue("high-priority")
->withTries(10)
->delay(60)
->shouldBeEncrypted()
->onConnection("redis")
->execute($order);All Fluent Methods
Available on both onQueue() and prepare():
| Method | Purpose |
|---|---|
onQueue(string|UnitEnum) | Target queue name |
onConnection(string|UnitEnum) | Target connection |
delay(array|int|DateTime|DateInterval) | Delay before first execution |
withoutDelay() | Remove any delay |
withTries(int) | Max retry attempts |
withBackoff(int|array) | Backoff in seconds (or array for exponential) |
withDisplayName(string) | Custom name for queue UI |
timeout(int) | Job timeout in seconds |
maxExceptions(int) | Max exceptions before stopping |
failOnTimeout() | Mark as failed on timeout (instead of retry) |
deleteWhenMissingModels() | Skip job if serialized model is deleted |
shouldBeEncrypted() | Encrypt job payload |
shouldRetryUntil(DateTime) | Stop retrying after this time |
chain(array) | Append a job chain |
Available on onQueue() only:
| Method | Purpose |
|---|---|
allOnQueue(string|UnitEnum) | Queue for job + entire chain |
allOnConnection(string|UnitEnum) | Connection for job + entire chain |
afterCommit() | Dispatch after DB transaction commits |
beforeCommit() | Dispatch immediately (don't wait for commit) |
shouldBeUnique() | Lock until job completes |
shouldBeUniqueUntilProcessing() | Lock until processing starts |
withUniqueId(string|int) | Custom unique lock identifier |
withUniqueFor(int) | Unique lock duration in seconds |
For execution modifiers (when(), unless()), see the dedicated page.
Configuration Priority
| Priority | Source | Example |
|---|---|---|
| 1 (lowest) | Properties / interfaces / attributes | public int $tries = 3 |
| 2 | configure() hook | $config->tries(5) |
| 3 | Named methods | tries(): int |
| 4 (highest) | Dispatch-time fluent calls | ->withTries(10) |
Tags, Middleware & Failure Handling
These methods don't participate in the configuration priority above, but they share the same argument matching behavior. Each method only needs to declare the execute-parameters it uses.
tags()
If you use Laravel Horizon, tags are auto-generated from any Eloquent model in your execute arguments (e.g. App\Models\User:1). This is standard Horizon behavior. Define a tags() method to override them.
middleware()
All Laravel queue middleware works unchanged. You can define middleware as a property, a method, or both. They are merged:
// Always applied
public array $middleware = [WithoutOverlapping::class];
// $priority matched by name, merged with the property above
public function middleware(string $priority): array
{
return [new RateLimited("orders-{$priority}")];
}uniqueVia()
When using unique jobs, Laravel uses the default cache store to acquire the lock. As with Laravel jobs, define a uniqueVia() method to return a different cache store:
public function uniqueVia(Order $order): Repository
{
return Cache::store("redis");
}failed()
Like Laravel jobs, executables support a failed() method. The difference: beyond the Throwable first parameter, it also receives your execute-arguments by name:
class ProcessOrder
{
use QueueableExecutable;
public function execute(string $orderId, int $amount): void
{
// ...
}
public function failed(Throwable $exception, string $orderId, int $amount): void
{
Log::error("Order {$orderId} failed (amount: {$amount})", [
"exception" => $exception->getMessage(),
]);
OrderFailureNotification::send($orderId);
}
}The first parameter must be typed as Throwable. You only need to declare the execute-parameters you need.
