diff --git a/src/Illuminate/Bus/Batch.php b/src/Illuminate/Bus/Batch.php index 8eb916094c79..06b832dd6a48 100644 --- a/src/Illuminate/Bus/Batch.php +++ b/src/Illuminate/Bus/Batch.php @@ -161,18 +161,37 @@ public function fresh() */ public function add($jobs) { - $jobs = Collection::wrap($jobs)->map(function ($job) { + $jobTotal = 0; + $jobs = Collection::wrap($jobs)->map(function ($job) use (&$jobTotal) { if ($job instanceof Closure) { $job = CallQueuedClosure::create($job); } - $job->withBatchId($this->id); + if (is_array($job)) { + $jobChain = $job; + $batchId = $this->id; + array_walk($jobChain, function (&$job) use ($batchId) { + if ($job instanceof Closure) { + $job = CallQueuedClosure::create($job); + } + $job->withBatchId($batchId); + }); + + $jobTotal += count($jobChain); + + $chainHead = array_shift($jobChain); + + return $chainHead->chain($jobChain); + } else { + $job->withBatchId($this->id); + $jobTotal++; + } return $job; }); - $this->repository->transaction(function () use ($jobs) { - $this->repository->incrementTotalJobs($this->id, count($jobs)); + $this->repository->transaction(function () use ($jobs, $jobTotal) { + $this->repository->incrementTotalJobs($this->id, $jobTotal); $this->queue->connection($this->options['connection'] ?? null)->bulk( $jobs->all(), diff --git a/tests/Bus/BusBatchTest.php b/tests/Bus/BusBatchTest.php index 3a7c7e8056de..381200daa171 100644 --- a/tests/Bus/BusBatchTest.php +++ b/tests/Bus/BusBatchTest.php @@ -8,10 +8,13 @@ use Illuminate\Bus\BatchFactory; use Illuminate\Bus\DatabaseBatchRepository; use Illuminate\Bus\PendingBatch; +use Illuminate\Bus\Queueable; use Illuminate\Container\Container; use Illuminate\Contracts\Queue\Factory; +use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\CallQueuedClosure; use Mockery as m; use PHPUnit\Framework\TestCase; @@ -300,6 +303,41 @@ public function test_batch_state_can_be_inspected() $this->assertTrue(is_string(json_encode($batch))); } + public function test_chain_can_be_added_to_batch() + { + $queue = m::mock(Factory::class); + + $batch = $this->createTestBatch($queue); + + $chainHeadJob = new ChainHeadJob(); + + $secondJob = new SecondTestJob(); + + $thirdJob = new ThirdTestJob(); + + $queue->shouldReceive('connection')->once() + ->with('test-connection') + ->andReturn($connection = m::mock(stdClass::class)); + + $connection->shouldReceive('bulk')->once()->with(\Mockery::on(function ($args) use ($chainHeadJob, $secondJob, $thirdJob) { + return + $args[0] == $chainHeadJob + && serialize($secondJob) == $args[0]->chained[0] + && serialize($thirdJob) == $args[0]->chained[1]; + }), '', 'test-queue'); + + $batch = $batch->add([ + [$chainHeadJob, $secondJob, $thirdJob], + ]); + + $this->assertEquals(3, $batch->totalJobs); + $this->assertEquals(3, $batch->pendingJobs); + $this->assertTrue(is_string($chainHeadJob->batchId)); + $this->assertTrue(is_string($secondJob->batchId)); + $this->assertTrue(is_string($thirdJob->batchId)); + $this->assertInstanceOf(CarbonImmutable::class, $batch->createdAt); + } + protected function createTestBatch($queue, $allowFailures = false) { $repository = new DatabaseBatchRepository(new BatchFactory($queue), DB::connection(), 'job_batches'); @@ -345,3 +383,18 @@ protected function schema() return $this->connection()->getSchemaBuilder(); } } + +class ChainHeadJob implements ShouldQueue +{ + use Dispatchable, Queueable, Batchable; +} + +class SecondTestJob implements ShouldQueue +{ + use Dispatchable, Queueable, Batchable; +} + +class ThirdTestJob implements ShouldQueue +{ + use Dispatchable, Queueable, Batchable; +}