Laravel: How to structure your jobs, commands, and sometimes Nova actions when using all three.

I've been unsettled by whether or not to use jobs or commands and if I use both where should the logic originate from.

But now, I've figured it out!

I like jobs because they can be queued, and that's pretty dope!

I like commands because I can run them manually from the CLI and I can use progress bars and output to know exactly what I'm dealing with. Especially for those big tasks!

My dilemma-- where should the main logic reside? I tried an action class so that I could call that from all the places, but you still lose the ability to really hook into the logic for CLI output and progress bars. So, it felt mostly useless.

Then for awhile, I started writing all my logic in the job class and wrapping my jobs up in a command in routes/console.php

I created a little helper to give me some of the visibility I was craving when I needed to manually run one of my jobs from the CLI.


if (!function_exists('commandWrapper')) {
    function commandWrapper($artisan, $operation) {
        $startTime = microtime(true);
        try {
        } catch (Exception $exception) {
        } finally {
            $endTime = microtime(true);
            $artisan->info(round($endTime - $startTime) . ' seconds to complete.');
            return 0;

And I would end up with something like this and it was fine. But, I still wasn't totally satisfied.

Artisan::command('ck:update-salsify-inventory-counts', function () {
    commandWrapper($this, function () {
        (new UpdateSalsifyInventoryCounts)->handle();

The Perfect Solution Was Born

Commanding Jobs.

Start in the command! Duhh. By using Artisan::call('command'); you can call your command where ever and when ever you want, while all the while maintaining your ability to run it manually from the CLI and get beautiful output.

Output from using progressBar in a Laravel Command


  1. Create a command with all my logic
  2. Create a job and I call my command from the job
  3. If I need to schedule the job, which I sometimes do, I will schedule the job not the command
  4. If I'm using Laravel Nova and need to setup an action, I'll call my command from that handler Artisan::call(), where then I can manage the queue-ability right from that action.