diff --git a/ChainedBatch.php b/ChainedBatch.php new file mode 100644 index 0000000..6d4f921 --- /dev/null +++ b/ChainedBatch.php @@ -0,0 +1,130 @@ +jobs = static::prepareNestedBatches($batch->jobs); + + $this->name = $batch->name; + $this->options = $batch->options; + } + + /** + * Prepare any nested batches within the given collection of jobs. + * + * @param \Illuminate\Support\Collection $jobs + * @return \Illuminate\Support\Collection + */ + public static function prepareNestedBatches(Collection $jobs): Collection + { + return $jobs->map(fn ($job) => match (true) { + is_array($job) => static::prepareNestedBatches(collect($job))->all(), + $job instanceof Collection => static::prepareNestedBatches($job), + $job instanceof PendingBatch => new ChainedBatch($job), + default => $job, + }); + } + + /** + * Handle the job. + * + * @return void + */ + public function handle() + { + $batch = new PendingBatch(Container::getInstance(), $this->jobs); + + $batch->name = $this->name; + $batch->options = $this->options; + + if ($this->queue) { + $batch->onQueue($this->queue); + } + + if ($this->connection) { + $batch->onConnection($this->connection); + } + + $this->dispatchRemainderOfChainAfterBatch($batch); + + foreach ($this->chainCatchCallbacks ?? [] as $callback) { + $batch->catch(function (Batch $batch, ?Throwable $exception) use ($callback) { + if (! $batch->allowsFailures()) { + $callback($exception); + } + }); + } + + $batch->dispatch(); + } + + /** + * Move the remainder of the chain to a "finally" batch callback. + * + * @param \Illuminate\Bus\PendingBatch $batch + * @return + */ + protected function dispatchRemainderOfChainAfterBatch(PendingBatch $batch) + { + if (! empty($this->chained)) { + $next = unserialize(array_shift($this->chained)); + + $next->chained = $this->chained; + + $next->onConnection($next->connection ?: $this->chainConnection); + $next->onQueue($next->queue ?: $this->chainQueue); + + $next->chainConnection = $this->chainConnection; + $next->chainQueue = $this->chainQueue; + $next->chainCatchCallbacks = $this->chainCatchCallbacks; + + $batch->finally(function (Batch $batch) use ($next) { + if (! $batch->cancelled()) { + Container::getInstance()->make(Dispatcher::class)->dispatch($next); + } + }); + + $this->chained = []; + } + } +} diff --git a/Dispatcher.php b/Dispatcher.php index 8ed3a21..86b19df 100644 --- a/Dispatcher.php +++ b/Dispatcher.php @@ -163,6 +163,7 @@ public function batch($jobs) public function chain($jobs) { $jobs = Collection::wrap($jobs); + $jobs = ChainedBatch::prepareNestedBatches($jobs); return new PendingChain($jobs->shift(), $jobs->toArray()); }