web-dev-qa-db-ja.com

Symfonyコンソールコマンドに依存関係を挿入するにはどうすればよいですか?

私はいくつかのSymfonyコンポーネントを使用し、Symfony Consoleコンポーネントを使用してシェルとやり取りするオープンソースアプリケーションを作成しています。

しかし、Logger、Configオブジェクト、Yamlパーサーなどの依存関係(すべてのコマンドで使用)を挿入する必要があります。Symfony\Component\Console\Command\Commandクラスを拡張することでこの問題を解決しました。しかし、これは単体テストを難しくし、正しい方法に見えません。

どうすればこれを解決できますか?

36
osm
_use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
_

CommandクラスをContainerAwareCommandから拡張し、$this->getContainer()->get('my_service_id');でサービスを取得します

23
Ramon

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);    
    }
}
28
Isengo

コンテナー自体を注入するのではなく、コンテナーからオブジェクトにサービスを注入することをお勧めします。 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からの上記の回答を実装するには、それを使用する必要があります。少なくともこの方法で、コマンドを適切にユニットテストできます。

17
orourkedd

次のようにPSR-11コンテナを提供するためにContainerCommandLoaderを使用できます。

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を提供する必要があります。

2
Elie Nehmé

私はsymfony2.8を代弁しています。 ContainerAwareCommandを拡張するクラスにコンストラクターを追加することはできません。拡張クラスには$this->getContainer()があり、コンストラクターを介してサービスを注入するのではなく、サービスを取得するためにカバーしました。

あなたは$this->getContainer()->get('service-name');を行うことができます

1
Olotin Temitope

Services.yamlに移動します

これをファイルに追加します(例として2つの既存のサービスを使用しました)。

App\Command\MyCommand:
        arguments: [
            '@request_stack',
            '@doctrine.orm.entity_manager'
        ]

ルートプロジェクトフォルダーでターミナルに入力するすべてのサービスのリストを表示するには:

php bin/console debug:autowiring --all

使用できるサービスの長いリストが表示されます。1行の例は次のようになります。

 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;
    }
}
0
Stas Sorokin