I have a series of batches, that need to be run in a chain (in correct order) and I am trying to find a way to link them all back together and ultimately give the user notification that everything is completed.
I currently am using the following:
Bus::batch(new \App\Jobs\TestBatchJob())
->allowFailures()
->name('test-batch-1')
->then(function(\Illuminate\Bus\Batch $batch) {
Bus::batch(new \App\Jobs\TestBatchJob2())
->allowFailures()
->name('test-batch-2')
->then(function (\Illuminate\Bus\Batch $batch){
Bus::batch(new \App\Jobs\TestBatchJob2())
->allowFailures()
->name('test-batch-3')
->then(function (\Illuminate\Bus\Batch $batch){
Bus::batch(new \App\Jobs\TestBatchJob2())
->allowFailures()
->name('test-batch-4')
->then(function (\Illuminate\Bus\Batch $batch){
// Batch Chain Finishes Here.
\Illuminate\Support\Facades\Auth::user()->notify(...);
})
->dispatch();
})
->dispatch();
})
->dispatch();
})
->dispatch();
This is OK... but the major downside is that it makes multiple batches.
I can no longer interact with the batch i.e. $batch->cancel() and any progress indicator on the frontend no longer works.
I need to ensure the first batch of jobs are completed before starting the second but want to tie all batches together somehow. Something like this (doesn't work) maybe explains my idea.
Bus::batch(new \App\Jobs\TestBatchJob())
->allowFailures()
->name('test-batch-1')
->then(function(\Illuminate\Bus\Batch $batch){
$batch->add(new \App\Jobs\TestBatchJob2());
})
->dispatch();
Update 1 - Bus::Chain()
Thanks to @boolfalse for the idea here. I tried a few things.
Basic Chain - Doesn't work
You can create batches in a chain, but they are still run in parallel. The bus chain method (with batches) is only useful if you have a batch at the very end of a set of chained jobs.
Note that the chain will only catch failure when creating the batch, not running the jobs inside of it.
Bus::chain([
function(){
Bus::batch(new TestBatchJob())->name('batch-1')->dispatch();
},
function(){
Bus::batch(new TestBatchJob())->name('batch-2')->dispatch();
},
function(){
Bus::batch(new TestBatchJob())->name('batch-3')->dispatch();
},
])->dispatch();
Chain with nested batches
One use case for the chain with nested batches is to allow us to run some normal jobs and then work with the processed data, before running a batch.
To run batches in a chain, we must use the then method on the chain.
$model = Model::create();
Bus::chain([
new TestBatchJob($model), // Run first on its own
function() use($model){
$model->doSomething(); // Amend the model prior to next batch
Bus::batch(new TestBatchJob($model)) // Run second on its own, using up-to-date model.
->name('batch-2')
->then(function(){
Bus::batch(new TestBatchJob())->name('batch-3')->dispatch(); // run third on its own
})
->dispatch();
}
])->dispatch();
Update 2 - Database Solution
For now, I am going back to my original solution of the nested then()s. To link each child job back to the parent (which we can display on the UI and track as one) I will be adding a column to the database job_batches table to link the child batches.
$batch = Bus::batch(new \App\Jobs\TestBatchJob())
->name('test-batch-1')
->then(function(Batch $parentBatch) {
$parent = $parentBatch->id;
$child = Bus::batch(new \App\Jobs\TestBatchJob2())
->name('test-batch-2')
->then(function (Batch $batch) use($parent){
$child = Bus::batch(new \App\Jobs\TestBatchJob2())
->name('test-batch-3')
->then(function(Batch $batch) use($parent){
$child = Bus::batch(new \App\Jobs\TestBatchJob2())
->name('test-batch-3')
->dispatch();
JobBatch::findByUuid($parent)->addChild($child);
})
->dispatch();
JobBatch::findByUuid($parent)->addChild($child);
})
->dispatch();
JobBatch::findByUuid($parent)->addChild($child);
})
->dispatch();
return JobBatch::query()->find($batch->id);

