Imagine a sequence of actions to be performed "in the background", in addition to creating the main logic.
For example, after user registration, you want to prepare demo data for their dashboard. You may want to put it into the background queue so the user won't wait for the operation to finish.
This is where Job classes come to help you.
Jobs are similar to Actions we discussed earlier, but Jobs may be put in a Queue. And they have Artisan command to create them:
php artisan make:job NewUserDataJob
And our job would look like this:
app/Jobs/NewUserDataJob.php:
class NewUserDataJob implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct(public User $user) {} public function handle() { Project::create([ 'user_id' => $this->user->id, 'name' => 'Demo project 1', ]); Category::create([ 'user_id' => $this->user->id, 'name' => 'Demo category 1', ]); Category::create([ 'user_id' => $this->user->id, 'name' => 'Demo category 2', ]); }}
All that's left is to dispatch the Job in the Controller:
use App\Jobs\NewUserDataJob; // ... public function store(StoreUserRequest $request){ $user = (new CreateUserAction())->execute($request->validated()); NewUserDataJob::dispatch($user); // ...}
Then, you need to configure everything separately around the queue. This course is about the structure of the code and not about queues specifically, so for that topic, I have two separate courses:
There's not much more to say about Jobs. Let's just take a look at more practical examples.
The first job example is from crater-invoice/crater open-source project for generating a PDF.
This example is very common, as generating PDFs can take time, especially if generating many PDF invoices is a monthly task.
So, in this example, the Job accepts the invoice as a parameter and generates the invoice in the handle()
method.
app/Jobs/GenerateInvoicePdfJob.php:
class GenerateInvoicePdfJob implements ShouldQueue{ use Dispatchable; use InteractsWithQueue; use Queueable; use SerializesModels; public $invoice; public $deleteExistingFile; public function __construct($invoice, $deleteExistingFile = false) { $this->invoice = $invoice; $this->deleteExistingFile = $deleteExistingFile; } public function handle() { $this->invoice->generatePDF('invoice', $this->invoice->invoice_number, $this->deleteExistingFile); return 0; }}
Then, in the Controller, the GenerateInvoicePdfJob
Job is dispatched when the invoice is created.
app/Http/Controllers/V1/Admin/Invoice/InvoicesController.php:
class InvoicesController extends Controller{ // ... public function store(Requests\InvoicesRequest $request) { $this->authorize('create', Invoice::class); $invoice = Invoice::createInvoice($request); if ($request->has('invoiceSend')) { $invoice->send($request->subject, $request->body); } GenerateInvoicePdfJob::dispatch($invoice); return new InvoiceResource($invoice); } // ...}
The second example is from protonemedia/eddy-server-management, an open-source project. This project has many jobs.
One of the jobs is to add an SSH key to a server, which could take some time while connecting to the server.
app/Jobs/AddSshKeyToServer.php:
class AddSshKeyToServer implements ShouldQueue{ use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function __construct(public SshKey $sshKey, public Server $server) { // } public function handle(): void { $this->server->runTask( new AuthorizePublicKey($this->server, $this->sshKey->public_key) )->asUser()->inBackground()->dispatch(); }}
Then, in the Controller for each server, the Job is dispatched to add the SSH key to a server.
app/Http/Controllers/AddSshKeyToServerController.php:
class AddSshKeyToServerController extends Controller{ // ... public function store(Request $request, SshKey $sshKey) { $request->validate([ 'servers' => ['required', 'array', 'min:1'], 'servers.*' => ['required', Rule::exists('servers', 'id')->where(function ($query) { $query->where('team_id', $this->team()->id); })], ]); $request->collect('servers')->each(function ($serverId) use ($sshKey) { $server = $this->team()->servers()->findOrFail($serverId); dispatch(new AddSshKeyToServer($sshKey, $server)); }); Toast::message(__('The SSH Key will be added to the selected servers. This may take a few minutes.')); return to_route('ssh-keys.index'); }}
So, a Job is a class you actively dispatch to perform some action. But what if you want to just "inform the system" that something happened and then let others decide what jobs to perform? This is where the Events and Listeners come into the scene in the next lesson.