如何将依赖项注入Symfony控制台命令?

时间:2011-09-29 13:18:44

标签: symfony dependency-injection console

我正在编写一个开源应用程序使用一些Symfony组件,并使用Symfony Console组件与shell进行交互。

但是,我需要注入依赖项(在所有命令中使用),如Logger,Config对象,Yaml解析器......我通过扩展Symfony\Component\Console\Command\Command类解决了这个问题。但这使得单元测试变得更难,而且看起来不正确。

我该如何解决这个问题?

7 个答案:

答案 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;
}