I have been using symfony/console for making commands and registering them like that, everything works fine:
bin/console:
#!/usr/bin/env php
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use App\Commands\LocalitiesCommand;
use Symfony\Component\Console\Application;
$app = new Application();
$app->add(new LocalitiesCommand(new LocalitiesGenerator()));
$app->run();
src/Commands/LocalitiesCommand.php:
<?php
declare(strict_types=1);
namespace App\Commands;
use App\LocalitiesGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class LocalitiesCommand extends Command
{
protected static $defaultName = 'app:generate-localities';
public function __construct(private LocalitiesGenerator $localitiesGenerator)
{
parent::__construct();
}
protected function configure(): void
{
$this
->setDescription('Generate localities.json file')
->setHelp('No arguments needed.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->localitiesGenerator->generateJsonLocalities();
$output->writeln("File localities.json generated!");
return Command::SUCCESS;
}
}
Now I want to autoinject the service with symfony/dependency-injection, I was reading the documentation and did some changes:
new bin/console:
#!/usr/bin/env php
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use App\Commands\LocalitiesCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/src/config'));
$loader->load('services.yaml');
$container->compile();
$app = new Application();
$app->add(new LocalitiesCommand());
$app->run();
config/services.yaml:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
But still asks me to add my service in the constructor when I instantiate my command. Why is it not working?
First, let's clear up a misconception:
If you call
new Foo(), then you no longer are getting autowired DI benefits. If you want to use autowire and automatic dependency injection, you need to let Symfony work for you. When you callnew, you are instantiating the object manually, and you need to take care of DI on your own.With that out of the way, how would you get to do this?
First,
composer.jsonwith the basic dependencies and autoloader declaration:The full directory structure will end up being like this:
Now, each of the parts:
The
composer.jsonfile with all the dependencies and autoloader:The front-controller script, the file running the application (
app, in my case):The service container configuration for the project:
One
FooCommandclass:The above depends on the
App\Text\Reverserservice, which will be injected automatically for us by the DI component:After installing and dumping the autoloader, by executing
php app(1) I get that thefoocommand is available (2):I can execute
php app foo, and the command is executed correctly, using its injected dependencies:A self-contained Symfony Console application, with minimal dependencies and automatic dependency injection.
(All the code for a very similar example, here).