Job Chaining is used to run series of Laravel jobs one after another that depend on previous job. If one job of the chain failed rest will not executed.
- Used to execute series of job that has dependencies with previous jobs.
- Execution of jobs stops only if one of the job in the chain failed to execute.
- using $this->delete() on jobs does not stop executing other jobs.
Table of Content
How to run a Job Chain by defining a queue connection and the queue?
Bus::chain([
new JobTaskOne,
new JobTaskTwo,
new JobTaskThree,
new JobTaskFour
])->onConnection('redis')->onQueue('podcasts')->dispatch();
Example: Running a series of jobs without chaining
Let’s see what happen if we ran series of jobs without adding it to a job chain. First setup a fresh Laravel application. Then create 4 Laravel job classes using below artisan command.
php artisan make:job JobTaskOne
php artisan make:job JobTaskTwo
php artisan make:job JobTaskThree
php artisan make:job JobTaskFour
Add logs on all 4 job classes in the handle() method like below. Modify JobTaskTwo and JobTaskFour by adding logs like below example.
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class JobTaskOne implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info('Completed - Job Task One');
}
}
Modify JobTaskThree to throw exception during the execution to mark it as a failed job.
<?php
namespace App\Jobs;
use Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class JobTaskThree implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info('Completed - Job Task Three');
throw new Exception();
}
}
Now dispatch all four job classes. Open web.php file and add below code.
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use App\Jobs\JobTaskOne;
use App\Jobs\JobTaskTwo;
use App\Jobs\JobTaskThree;
use App\Jobs\JobTaskFour;
Route::get('/', function () {
//Log request
Log::info("Received a get request on root route");
JobTaskOne::dispatch();
JobTaskTwo::dispatch();
JobTaskThree::dispatch();
JobTaskFour::dispatch();
});
Now run Laravel web application
php artisan serve
Run queue worker
php artisan queue:listen
See Laravel logs.
All the Laravel Jobs executed even if a job on the chain is failed.
[2024-12-28 09:26:31] local.INFO: Received a get request on root route
[2024-12-28 09:26:34] local.INFO: Completed - Job Task One
[2024-12-28 09:26:34] local.INFO: Completed - Job Task Two
[2024-12-28 09:26:35] local.INFO: Completed - Job Task Three
[2024-12-28 09:26:35] local.ERROR: {"exception":"[object] (Exception(code: 0): ..
[2024-12-28 09:26:36] local.INFO: Completed - Job Task Four
Example: Running series of Jobs using Job Chaining
Task:
Run 4 Laravel jobs in a Job chain. Purposely fail third job of the chain. Make sure to catch any exceptions thrown by jobs in the job chain.
Open web.php file and add all four Laravel jobs to chain() method provided by Bus facade. Use catch() method to catch any exceptions thrown by jobs on the job chain.
<?php
use App\Jobs\JobTaskFour;
use App\Jobs\JobTaskOne;
use App\Jobs\JobTaskThree;
use App\Jobs\JobTaskTwo;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
//Log request
Log::info("Received a get request on root route");
Bus::chain([
new JobTaskOne,
new JobTaskTwo,
new JobTaskThree,
new JobTaskFour
])->catch(function (Throwable $e)
{
Log::info("Catch Exception: ".$e->getMessage());
})
->dispatch();
});
Now run Laravel application and queue listener using artisan command and check the log file.
[2024-12-28 10:28:26] local.INFO: Received a get request on root route
[2024-12-28 10:28:28] local.INFO: Completed - Job Task One
[2024-12-28 10:28:29] local.INFO: Completed - Job Task Two
[2024-12-28 10:28:29] local.INFO: Completed - Job Task Three
[2024-12-28 10:28:29] local.INFO: Catch Exception: My Custom Exception message
[2024-12-28 10:28:29] local.ERROR: My Custom Exception message {"exception":"[object]..
With chain() method you will after JobTaskThree failed JobTaskFour is not executed. 5th line shows the thrown exception.
Example: Add job to end of the chain (AppendToChain)
Create another job to append to job chain.
php artisan make:job ExtraJobTask
Add log to handle() method of the ExtraJobTask job class.
<?php
namespace App\Jobs;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class ExtraJobTask implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info('Completed - extra job task');
}
}
Now append this job after completing the JobTaskThree. For that open JobTaskThree.php file and modify like below.
- Remove previously defined exception.
- Add ExtraJobTask class to appendToChain() method.
<?php
namespace App\Jobs;
use Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class JobTaskThree implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info('Completed - Job Task Three');
$this->appendToChain(new ExtraJobTask);
}
}
Now run this Laravel application and queue listener using the artisan command. Does not need to modify web.php file.
Logs:
[2024-12-28 10:09:31] local.INFO: Received a get request on root route
[2024-12-28 10:09:33] local.INFO: Completed - Job Task One
[2024-12-28 10:09:34] local.INFO: Completed - Job Task Two
[2024-12-28 10:09:34] local.INFO: Completed - Job Task Three
[2024-12-28 10:09:35] local.INFO: Completed - Job Task Four
[2024-12-28 10:09:36] local.INFO: Completed - extra job task
Even though ExtraJobTask defined inside the JobTaskThree it will execute after JobTaskFour(last job of the chain)
Example: Run a Job immediately after a selected job from a Job chain (prependToChain)
You can use prependToChain() method to run given job immediately after the defined job of the job chaining.
Open JobTaskThree and add below code.
<?php
namespace App\Jobs;
use Exception;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
class JobTaskThree implements ShouldQueue
{
use Queueable;
/**
* Create a new job instance.
*/
public function __construct()
{
//
}
/**
* Execute the job.
*/
public function handle(): void
{
Log::info('Completed - Job Task Three');
$this->prependToChain(new ExtraJobTask);
}
}
Run this application and queue listener. No need to modify web.php file.
Logs:
[2024-12-28 10:13:19] local.INFO: Received a get request on root route
[2024-12-28 10:13:23] local.INFO: Completed - Job Task One
[2024-12-28 10:13:23] local.INFO: Completed - Job Task Two
[2024-12-28 10:13:24] local.INFO: Completed - Job Task Three
[2024-12-28 10:13:25] local.INFO: Completed - extra job task
[2024-12-28 10:13:25] local.INFO: Completed - Job Task Four
ExtraJobTask was defined inside the JobTaskThree using prependToChain() method. Therefore ExtraJobTask executed right after completing the defined job class of the chain and continue executing rest of the jobs of the chain.