DEV Community

david duymelinck
david duymelinck

Posted on

4 2

Symfony: Invokable command

The upcoming release of Symfony, 7.3, introduces invokable commands.

For me this is exciting news because more and more I believe that if a class has a single public function it should be an invokable class.
I think it reduces naming things. Now there is a lot of code that uses method names like execute or handle as the single entry point for the class.
Another reason is that with an invokable class there can't be a misunderstanding what the method is that is going to be executed.

While I was doing research how this could affect Laravel commands, which are an extension of the Symfony commands, I found out it already was possible to use __invoke, but in the documentation and using artisan make:command the handle method is the default.

Together with the invokable commands the CLI arguments and options can now be method arguments.

// before
class CreateUserCommand extends Command
{
    protected function configure(): void
    {
        $this->addArgument('name', InputArgument::REQUIRED);
        $this->addOption('activate', null, InputOption::VALUE_NONE);
    }
}
// after
class CreateUserCommand
{
    public function __invoke(
        SymfonyStyle $io, 
        #[Argument] string $name, 
        #[Option] bool $activate = false,
    ): int
    { // code here }
}
Enter fullscreen mode Exit fullscreen mode

Next to removing the need for the configure method, it also means no more $name = $input->getArgument('name'); lines.

I never liked the Laravel command signature property because it is text that gets parsed to the configure method content and the name property.
This violates the single responsibility principle.

So for me Symfony 7.3 is already a release I'm wanting to use.
I'm wondering when Laravel is going to move to the new functionality. I think the CLI arguments and options are too big of a code reduction to ignore for long.

Top comments (2)

Collapse
 
tamajit_modak profile image
TAMAJIT MODAK

Looks like Symfony just said, “Goodbye configure(), hello clean code.”

Finally, we can stop arguing whether it's handle() or execute() and just let __invoke() do its thing. Laravel better start taking notes before we all start Symfony-fying our artisan commands :)

Collapse
 
xwero profile image
david duymelinck • Edited

If the post gave the impression that all the commands need to change, that was not my intention. The current way will stay as is. In Symfony 7.3 there is the option to do it "new style".

In regard of Symfony-fying artisan commands.

use Illuminate\Console\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand('app:test')]
class TestCommand extends Command
{
    protected function configure()
    {
        $this->addArgument('name', InputArgument::REQUIRED);
    }

    public function __invoke(InputInterface $input, OutputInterface $output): int
    {
        $name = $input->getArgument('name');

        $output->writeln("Test command name: $name");
        return Command::SUCCESS;
    }
}
Enter fullscreen mode Exit fullscreen mode

This command works in artisan.
Once Laravel uses the Symfony 7.3 console package, the Argument and Option attributes should work as well.
So for the people that know this, creating commands is going to be easier.

The question is when that change happened, is the Laravel documentation going to address the "new style" options or is it going to stick with the current way. And will the artisan make:command get an option to make a more Symfony "new style" bootstrapped class.