Add a required argument to every symfony/console command
Tags: symfony, console, argument, required
Published: 2020-06-03 13:44:40

I would assume that everyone has fiddled around with `symfony/console` right? If not, you should probably check it out [here](https://symfony.com/doc/current/components/console.html). It is pretty easy to use and understand.

One thing I had problems today is: How do you add a **required** argument for every command that is added to the console application?

I came up with 3 solutions:
1. Adding a `BaseCommand` class which every command added has to `extend` from
2. Add a required argument to the `Console\Application`
3. Add a required argument for each `Console\Command` added to the `Console\Application`

## TLDR;
Like President Schwarzenegger used to say in the Simpson Movie: "I pick number three".
This way for me is most robust change as you don't need to alter any command and every new command that will be added, will receive the required argument.

Just as a side note: I used to pick the first approach :)

## Adding a `BaseCommand` class which every command added has to `extend` from
This is pretty straight forward. You create a new `BaseCommand` class and add your required argument into the `configure()` method.

```php
<?php

use Symfony\Component\Console\Command\Command;

class BaseCommand extends Command 
{
    public function configure(): void
    {
        $this->addArgument(
            'name',
            InputArgument::REQUIRED,
            'description'
       );
    }
}
```

But, what this would require is you **have** to call `parent::configure()` in every command so that the argument is there, if not. Well its not.

## Add a required argument to the `Console\Application`
This was the second approach I found while searching for a solution.
Sadly, every solution I found was adding a new **optional** argument to the definition, which is not what I needed, you might need to "fiddle" around with the `setArguments()` of the `InputDefinition` of an application.

You can do this inside you bootstrap file, whery you load the application.

```php
$app = new Application();
$appDefinition = $app->getDefinition();

$appDefininition->setArguments(
    array_merge([$requiredArgument], $appDefinition->getArguments())
);
```

But this is kind of out of scope where you want to define it.
So you can create your custom application class and do it there:


```php
<?php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\InputDefinition;

class CustomApplication extends Application
{
    protected function getDefaultInputDefinition(): InputDefinition
    {
        $definition = parent::getDefaultInputDefinition();

        $definition->setArguments(
            array_merge([$requiredArgument], $definition->getArguments())
        );

        return $definition;
    }
}
```

Downside on this: You have a require argument for every command of your application even with the `HelpCommand` or `ListCommand`. So you can't run you application by simply running `bin/console` (if that is you entrypoint). You would need to pass the required argument everytime.

## Add a required argument for each `Console\Command` added to the `Console\Application`
The solution I choose, was to add this argument for each command added to the application.

Some of you might think: Wait a second, what abount `HelpCommand` and `ListCommand` you mentioned before?

Yes you are right, every command, except commands from `symfony/console` namespace :)

So this is my solution: Creating my `CustomApplication` and overwriting the `add(Command $command)` adding my required argument to every added command to my application.

```php
<?php

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;

class ConsoleApplication extends Application
{
    public function add(Command $command)
    {
        if (strpos(get_class($command), 'Symfony\Component\Console') === 0) {
            return parent::add($command);
        }

        $commandDefinition = $command->getDefinition();
        $commandDefinition->setArguments(
            array_merge([$requiredArgument], $commandDefinition->getArguments())
        );

        return parent::add($command);
    }
}
```

There you have it. Every command will get your required argument. Also this will be the first argument in every command. So even if you have an optional argument configured, there won't be a problem with that.

---

Hope that will help some of you solving the same problem I had :)
If you have another clever solution, let me know!