Ryan Chandler

Registering global arguments and options in Laravel Zero

2 min read

I recently ran into a scenario where I wanted to register a couple of global options inside of a Laravel Zero project. These options need to be available for all commands inside of my project, but I didn't want to extend a custom base Command class.

I tried a couple of things and eventually landed on the following:

use Illuminate\Console\Application;
use Symfony\Component\Console\Input\InputOption;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Application::starting(function (Application $application): void {
            $option = new InputOption(
                name: 'example',
                mode: InputOption::VALUE_REQUIRED,
                default: 'foo',
                suggestedValues: ['foo', 'bar', 'baz']
            );
            
            $application->getDefinition()->addOption($option);
        });
    }
}

Now every command that I add to my Laravel Zero application will have the --example option attached to it, allowing me to do things such as load config files from a specified location without needing to do that manually inside of each command, kind of like this:

use Illuminate\Console\Application;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Support\Facades\Event;
use App\Contracts\UserConfiguration;
use App\Configuration\ProjectConfiguration;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        /*
         * Register the `--config` option with every single command by default.
         * 
         * Default to `./config.json`, but accept a value from the user.
         */
        Application::starting(function (Application $application): void {
            $option = new InputOption(
                name: 'config',
                mode: InputOption::VALUE_REQUIRED,
                default: './config.json',
            );
            
            $application->getDefinition()->addOption($option);
        });

        /*
         * When a command is executed, the framework dispatches a `CommandStarting` event.
         * 
         * The event includes the parsed input (arguments & options), so I can use that to
         * bind an object to the container which can be used & injected outside of the command.
         */
        Event::listen(CommandStarting::class, function (CommandStarting $event) {
            $this->app->instance(
                UserConfiguration::class,
                ProjectConfiguration::parse($event->input->getOption('config'))
            );
        });
    }
}