dispatch() returns a PendingDispatch, pushed when the statement ends, that takes onConnection(), onQueue('emails'), delay(now()->plus(minutes: 10)) and afterCommit(); dispatchIf() adds a condition and Bus::bulk() pushes many jobs at once. A chain runs jobs in order and stops at the first that fails for good, which suits checkout:
Artisan::command('shop:checkout {order}', function (int $order) {
$order = Order::findOrFail($order);
Bus::chain([
new GenerateInvoice($order),
new SendOrderConfirmation($order),
(new NotifyWarehouse($order))->delay(now()->plus(seconds: 5)),
])->catch(function (Throwable $e) use ($order) {
Log::error("checkout chain for order {$order->id} stopped: {$e->getMessage()}");
})->dispatch();
$this->info("order {$order->id}: chain of 3 jobs queued");
});After php artisan shop:checkout 7, jobs held one row, GenerateInvoice; the rest of the chain rides in its payload. The worker finished the email at 08:57:54 and started the warehouse job at 08:58:00 (delay plus polling); with the warehouse down, its third failure ended the chain and fired catch. Chain callbacks are serialized from their source file; typed into tinker, the same chain failed with Call to a member function bindTo() on null.