spatie/laravel-artisan-dispatchable
Register Laravel jobs as Artisan commands by implementing the ArtisanDispatchable interface. Dispatch queued jobs via CLI (e.g., php artisan process-podcast) so long-running tasks won’t block the scheduler, while remaining runnable from Artisan.
Installation:
composer require spatie/laravel-artisan-dispatchable
Publish the config (optional):
php artisan vendor:publish --provider="Spatie\ArtisanDispatchable\ArtisanDispatchableServiceProvider"
Mark a Job as Artisan-Dispatchable:
Implement the ArtisanDispatchable interface in your job class:
use Spatie\ArtisanDispatchable\Jobs\ArtisanDispatchable;
class ProcessPodcast implements ShouldQueue, ArtisanDispatchable
{
public function handle() { /* ... */ }
}
First Use Case: Run the job directly via Artisan:
php artisan process-podcast
The job will execute asynchronously (if queued) or synchronously (if not queued).
ProcessPodcast → process-podcast).config/artisan-dispatchable.php for customizations (e.g., command naming conventions, middleware).php artisan to test jobs locally before scheduling them.Define the Job:
class SendWelcomeEmail implements ShouldQueue, ArtisanDispatchable
{
public function handle() {
Mail::to($user)->send(new WelcomeEmail($user));
}
}
Dispatch via Artisan:
php artisan send-welcome-email --user=123
--user=123 or --queue=high).Schedule via Laravel Scheduler:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule) {
$schedule->job(new SendWelcomeEmail($user))
->everyMinute();
}
Passing Arguments:
class ProcessPodcast implements ArtisanDispatchable {
public function __construct(public int $podcastId) {}
public static function command() {
return 'process-podcast';
}
}
Run with:
php artisan process-podcast --podcastId=42
Queue Configuration:
public function handle() {
Queue::connection('high')->push($this);
}
php artisan process-podcast --queue=high
Middleware:
class ProcessPodcast implements ArtisanDispatchable {
public function handle() {
// Middleware runs here
}
}
ArtisanDispatchableServiceProvider.Batching:
php artisan process-podcast --batch --ids=1,2,3
handleBatch() in your job:
public function handleBatch(array $podcastIds) { /* ... */ }
Event Listeners:
class ProcessPodcast implements ArtisanDispatchable {
public function handle() {
event(new JobStarted($this));
// Work...
event(new JobFinished($this));
}
}
Command Naming Conflicts:
MakeModel).command() method or config:
public static function command() {
return 'custom-process-podcast';
}
Argument Parsing:
handle():
public function handle() {
$this->podcastId = (int) $this->podcastId;
}
--json flag for complex data:
php artisan process-podcast --data='{"key":"value"}'
Queue vs. Sync Execution:
ShouldQueue, it runs synchronously during Artisan execution.ShouldQueue to force async behavior:
class ProcessPodcast implements ShouldQueue, ArtisanDispatchable { /* ... */ }
Dependency Injection:
resolve() or lazy-load dependencies in handle().Error Handling:
0 even if the job fails (unless thrown as an exception).try {
$this->handle();
} catch (\Throwable $e) {
\Log::error("Job failed: {$e->getMessage()}");
throw $e; // Re-throw to mark Artisan command as failed
}
Check Job Execution:
php artisan queue:work --once to debug jobs manually.tail -f storage/logs/laravel.log
Inspect Arguments:
handle():
public function handle() {
\Log::debug('Input:', $this->input());
}
Artisan Command Help:
public static function signature() {
return 'process-podcast {--user= : User ID} {--queue= : Queue name}';
}
public static function description() {
return 'Processes a podcast for a given user';
}
php artisan process-podcast --help
Custom Command Logic:
ArtisanDispatchableServiceProvider to add pre/post hooks:
public function boot() {
Artisan::resolving(function ($command) {
if ($command instanceof ArtisanDispatchableCommand) {
// Pre-execution logic
}
});
}
Dynamic Job Resolution:
$this->app->bind('command:custom-job', function () {
return new CustomJob();
});
Testing:
$job = new ProcessPodcast();
$this->artisan('process-podcast')
->expectsQuestion('Confirm?', 'yes')
->assertExitCode(0);
Laravel 10+ Compatibility:
ShouldQueue is imported from Illuminate\Contracts\Queue\ShouldQueue (not Illuminate\Bus\Queueable).Illuminate\Bus\Queueable for additional features like onQueue() or delay().How can I help you explore Laravel packages today?