php 115 lines · 3 tabs

Schedule a Recurring Report Job in Laravel's Console Kernel

Shared by codesnips Aug 2026
3 tabs
<?php

namespace App\Console\Commands;

use App\Jobs\CompileReportJob;
use Carbon\CarbonImmutable;
use Illuminate\Console\Command;

class GenerateWeeklyReport extends Command
{
    protected $signature = 'report:weekly {--date= : Anchor date inside the target week (Y-m-d)}';

    protected $description = 'Queue compilation of the weekly activity report';

    public function handle(): int
    {
        $anchor = $this->option('date')
            ? CarbonImmutable::parse($this->option('date'))
            : CarbonImmutable::now()->subWeek();

        $start = $anchor->startOfWeek();
        $end = $anchor->endOfWeek();

        CompileReportJob::dispatch($start, $end);

        $this->info(sprintf(
            'Queued weekly report for %s - %s',
            $start->toDateString(),
            $end->toDateString()
        ));

        return self::SUCCESS;
    }
}
3 files · php Explain with highlit

This snippet shows how a recurring report is wired up in Laravel: a custom Artisan command dispatches a queued job, and the scheduler in the console kernel decides when that command runs. The three tabs form one story — the command that is the entry point, the job that does the heavy lifting, and the kernel that schedules the command on a cron cadence.

In GenerateWeeklyReport command, the command is deliberately thin. Its handle() method resolves a date range from the --date option (defaulting to last week via a small helper) and then calls dispatch() on CompileReportJob. Keeping the command lightweight matters: Artisan commands run in a single foreground process, and doing report compilation inline would block the scheduler's schedule:run invocation and risk timeouts. By dispatching to a queue, the command returns almost immediately and the actual work happens in a worker. The command also writes to $this->info(...) so operators watching the scheduler log get feedback that the job was enqueued.

CompileReportJob is the queued unit of work. It implements ShouldQueue and pulls in Queueable/SerializesModels, so its constructor arguments ($start, $end) are serialized onto the queue and rehydrated in the worker. The $uniqueId() method combined with ShouldBeUnique is the key reliability detail: it prevents two overlapping reports for the same week from being processed at once, which protects against duplicate emails if the scheduler fires twice or a retry stacks up. $tries and $backoff give it bounded retries with a delay, and failed() logs the exception so a poisoned job is visible rather than silent.

Console Kernel ties it together. The schedule() method registers report:weekly with weeklyOn(1, '07:00') for Monday mornings, then chains guards that are easy to miss but important in production: timezone() pins the cron interpretation, withoutOverlapping() stops a slow run from colliding with the next tick, onOneServer() ensures only one node in a multi-server deployment fires it, and runInBackground() keeps schedule:run from blocking. A single system cron entry running schedule:run every minute is all that drives this. The trade-off is that correctness now depends on that cron plus a running queue worker, so the unique and overlap guards are what keep the pipeline from double-reporting when infrastructure hiccups.


Related snips

Share this code

Here's the card — post it anywhere.

Schedule a Recurring Report Job in Laravel's Console Kernel — share card
Link copied