如何将依赖项注入Symfony Console命令?

ckocjqey  于 2022-12-13  发布在  其他
关注(0)|答案(8)|浏览(498)

我正在编写一个开源应用程序,使用一些Symfony组件,并使用Symfony Console组件与shell交互。
但是,我需要注入依赖项(在所有命令中使用),比如Logger,Config对象,Yaml解析器...我通过扩展Symfony\Component\Console\Command\Command类解决了这个问题。但是这使得单元测试更难,而且看起来不正确。
我该如何解决这个问题?

pcrecxhr

pcrecxhr1#

自Symfony4.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);    
    }
}
mwg9r5ms

mwg9r5ms2#

最好不要注入容器本身,而是将容器中的服务注入到对象中。如果你使用的是Symfony 2的容器,那么你可以这样做:
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的答案,你必须使用它。至少这样你的命令可以被正确地进行单元测试。

bsxbgnwa

bsxbgnwa3#

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;

ContainerAwareCommand扩展Command类,并使用$this->getContainer()->get('my_service_id');获取服务

t3psigkw

t3psigkw4#

您可以使用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);
    }
...

注意:应在容器配置中提供ChangeMode。

pb3s4cty

pb3s4cty5#

我说的是symfony2.8。您不能向扩展ContainerAwareCommand的类添加构造函数,因为扩展的类有一个$this->getContainer(),它让您可以获取服务,而不是通过构造函数注入服务。
你可以做$this->getContainer()->get('service-name');

cl25kdpy

cl25kdpy6#

转到服务.yaml
将This添加到文件中(我使用了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令牌服务是您正在寻找的(例如),您将使用括号中的部分作为服务:(安全性.csrf.令牌存储)
所以你的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;
    }
}
siv3szwd

siv3szwd7#

在Symfony 3.4中,如果正确配置了autowire,则可以将服务注入到命令的构造函数中。

public function __construct(
    \AppBundle\Handler\Service\AwsS3Handler $s3Handler
) {
    parent::__construct();

    $this->s3Handler = $s3Handler;
}
aydmsdu9

aydmsdu98#

php 8.1下载

public function __construct(private EntityManagerInterface $em, string $name = null)
{
    parent::__construct($name);
}

相关问题