★ Improving Artisan commands

Written by murze.be - - Aggregated on Thursday November 7, 2019

In this small blog post, I'd like to give you a couple of tips to make your Artisan commands better.

Use the fully qualified class name to schedule commands #

You probably know that you can schedule artisan commands in the console kernel like this.

// app/Console/Kernel.php

class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('email-campaigns:send-scheduled-campaigns')->everyMinute();
    }
}

Did you know that you could use the fully qualified name of a command too?

class Kernel extends ConsoleKernel
{
    protected function schedule(Schedule $schedule)
    {
        $schedule->command(SendScheduledEmailCampaigns::class)->everyMinute();
    }
}

Using the fully qualified class name has some nice benefits. Any decent IDE will allow you to click through a command. Also, when performing a name refactor on the command, decent IDEs will automatically change the name of the command everywhere, including in the kernel.

Make commands smaller #

When you scaffold a command using make:command, the produced code is quite verbose. Here's an example:

class SendScheduledEmailCampaigns extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'email-campaigns:send-scheduled-campaigns';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
    
    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // do the work
    }
}

I like to make those commands as small as possible by removing any cruft that doesn't make the code clearer. Those comments are not necessary. Also, the $description is not mandatory, so you can lose that if the command name is already clear enough.

class SendScheduledEmailCampaigns extends Command
{
    protected $signature = 'email-campaigns:send-scheduled-campaigns';
    
    public function handle()
    {
        // do the work
    }
}

To me, this is more readable. We didn't lose any information by removing all those lines.

Use the handle method to inject dependencies #

I sometimes see the constructor is used to inject dependencies.

class SendScheduledEmailCampaigns extends Command
{
    protected $signature = 'email-campaigns:send-scheduled-campaigns';
    
    /** @var MyDependency **/
    protected $myDependency;
    
    public function __construct(MyDependency $myDependency) 
    {
       $this->myDependency = $myDependency;
    }
    
    public function handle()
    {
        // do something with `$this->myDependency`
    }
}

This approach is not ideal. It was recently brought to my attention that the constructor of each registered command will get executed when artisan is invoked.

To inject dependencies is much better to use the handle method. Everything you specify there will be resolved out of the IoC container when your command executes.

class SendScheduledEmailCampaigns extends Command
{
    protected $signature = 'email-campaigns:send-scheduled-campaigns';
        
    public function handle(MyDependency $myDependency)
    {
        // do something with `$myDependency`
    }
}

« Laravel Multidomain Package - Laravel News

php[architect] - The site for PHP professionals - Security Corner: Twist and Shout »