Symfony provides several ways to create console commands. The recommended
approach today is to use the #[AsCommand] attribute to register commands
directly in your command classes, keeping configuration simple and explicit.
However, when working on real-world applications, it’s common to have multiple
closely related commands that share the same dependencies (a repository, an API
client, a logger). Defining each command in its own class quickly leads to
repetitive boilerplate, especially duplicated constructors wiring the same services.
Symfony 8.1 solves this by letting you group related commands inside a
single class, applying the #[AsCommand] attribute to individual
methods. This mirrors how Symfony already handles multiple controller actions
in one class or multiple message handlers in one handler class:
use SymfonyComponentConsoleAttributeAsCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleOutputOutputInterface;
class UserCommands
{
public function __construct(
private UserRepository $users,
private LoggerInterface $logger,
) {
}
#[AsCommand('app:user:create', description: 'Creates a new user')]
public function create(OutputInterface $output): int
{
// ...
return Command::SUCCESS;
}
#[AsCommand('app:user:delete', description: 'Deletes an existing user')]
public function delete(OutputInterface $output): int
{
// ...
return Command::SUCCESS;
}
}
Each annotated method is registered as an independent command thanks to
autoconfiguration, so no extra wiring is needed. The constructor is defined
once, and every command in the class reuses the same injected dependencies.
When using the Console component standalone (without the service container),
register each method as a first-class callable:
$instance = new UserCommands($users, $logger);
$application = new Application();
$application->addCommand($instance->create(...));
$application->addCommand($instance->delete(...));
Testing works the same way. Pass the first-class callable to CommandTester
to exercise a single method:
$tester = new CommandTester((new UserCommands($users, $logger))->create(...));
$tester->execute([]);
$tester->assertCommandIsSuccessful();




