我正在编写一个开源应用程序使用一些Symfony组件,并使用Symfony Console组件与shell进行交互。
但是,我需要注入依赖项(在所有命令中使用),如Logger,Config对象,Yaml解析器......我通过扩展Symfony\Component\Console\Command\Command
类解决了这个问题。但这使得单元测试变得更难,而且看起来不正确。
我该如何解决这个问题?
答案 0 :(得分:26)
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
从 ContainerAwareCommand 扩展您的命令类,并使用$this->getContainer()->get('my_service_id');
答案 1 :(得分:16)
最好不要注入容器本身,而是将容器中的服务注入到对象中。如果您正在使用Symfony2的容器,那么您可以执行以下操作:
MyBundle / Resources / config / services(或您决定放置此文件的任何地方):
...
<services>
<service id="mybundle.command.somecommand" class="MyBundle\Command\SomeCommand">
<call method="setSomeService">
<argument type="service" id="some_service_id" />
</call>
</service>
</services>
...
然后你的命令类应如下所示:
<?php
namespace MyBundle\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use The\Class\Of\The\Service\I\Wanted\Injected;
class SomeCommand extends Command
{
protected $someService;
public function setSomeService(Injected $someService)
{
$this->someService = $someService;
}
...
我知道你说你没有使用依赖注入容器,但是为了从@ramon实现上述答案,你必须使用它。至少通过这种方式,您的命令可以进行适当的单元测试。
答案 2 :(得分:14)
自Symfony 4.2 起,不再使用 ContainerAwareCommand 。改用DI。
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Doctrine\ORM\EntityManagerInterface;
final class YourCommand extends Command
{
/**
* @var EntityManagerInterface
*/
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// YOUR CODE
$this->entityManager->persist($object1);
}
}
答案 3 :(得分:1)
您可以使用ContainerCommandLoader来提供PSR-11容器,如下所示:
require 'vendor/autoload.php';
$application = new Application('my-app', '1.0');
$container = require 'config/container.php';
// Lazy load command with container
$commandLoader = new ContainerCommandLoader($container, [
'app:change-mode' => ChangeMode::class,
'app:generate-logs' => GenerateLogos::class,
]);
$application->setCommandLoader($commandLoader);
$application->run();
ChangeMode类可以定义如下:
class ChangeMode extends Command
{
protected static $defaultName = 'app:change-mode';
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
parent::__construct(static::$defaultName);
}
...
NB .:容器配置中应提供ChangeMode。
答案 4 :(得分:1)
转到services.yaml
将此添加到文件中(我以2个现有服务为例):
App\Command\MyCommand:
arguments: [
'@request_stack',
'@doctrine.orm.entity_manager'
]
要查看所有服务的列表,请在根项目文件夹中的终端中输入:
php bin/console debug:autowiring --all
您将获得一长串可以使用的服务,其中一行示例如下:
Stores CSRF tokens.
Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface (security.csrf.token_storage)
因此,如果您正在寻找CSRF令牌服务(例如),您将使用括号中的部分作为服务:(security.csrf.token_storage)
所以您的services.yaml看起来会像这样:
parameters:
services:
_defaults:
autowire: true
autoconfigure: true
# Here might be some other services...
App\Command\MyCommand:
arguments: [
'@security.csrf.token_storage'
]
然后在您的命令类中使用构造函数中的服务:
class MyCommand extends Command
{
private $csrfToken;
public function __construct(CsrfToken $csrfToken)
{
parent::__construct();
$this->csrfToken = $csrfToken;
}
}
答案 5 :(得分:0)
我代表symfony2.8。您不能向扩展ContainerAwareCommand的类中添加构造函数,因为扩展类具有$this->getContainer()
,可以让您轻松获取服务,而不是通过构造函数注入它们。
您可以进行$this->getContainer()->get('service-name');
答案 6 :(得分:0)
在Symfony 3.4中,如果正确配置了自动装配,则可以将服务注入到命令的构造函数中。
public function __construct(
\AppBundle\Handler\Service\AwsS3Handler $s3Handler
) {
parent::__construct();
$this->s3Handler = $s3Handler;
}