1

我使用 Symfony 4.2 实现了策略模式,问题是下面的 addMethodCall 没有执行。

class ConverterPass implements CompilerPassInterface
{ 
    const CONVERSION_SERVICE_ID = 'crv.conversion';
    const SERVICE_ID = 'crv.converter';

    public function process(ContainerBuilder $container)
    {
        // check if the conversion service is even defined
        // and if not, exit early
        if ( ! $container->has(self::CONVERSION_SERVICE_ID)) {
            return false;
        }

        $definition = $container->findDefinition(self::CONVERSION_SERVICE_ID);

        // find all the services that are tagged as converters
        $taggedServices = $container->findTaggedServiceIds(self::SERVICE_ID);

        foreach ($taggedServices as $id => $tag) {
            // add the service to the Service\Conversion::$converters array
            $definition->addMethodCall(
                'addConverter',
                [
                    new Reference($id)
                ]
            ); 
        }
    } 
}

在 kernel.php 我得到了受保护的函数 build(ContainerBuilder $container) { $container->addCompilerPass( new ConverterPass() ); }

我的 ConverterInterface.php

namespace App\Converter;

interface ConverterInterface
{
    public function convert(array $data);
    public function supports(string $format);
}

然后是 ConvertToSela.php 和类似的另一个 Convert

namespace App\Converter;


class ConvertToSela implements ConverterInterface
{
    public function supports(string $format)
    {
        return $format === 'sela';
    }

    public function convert(array $data)
    {
        return 'sela';
    }
}

在我执行的 Conversion.php 中,我得到空数组,这意味着未调用 addConverter。

class Conversion
{

    private $converters;

    public function __construct()
    {
        $this->converters = [];
    }

    public function addConverter(ConverterInterface $converter)
    {
        dd($converter);
        $this->converters[] = $converter;

        return $this->converters;
    } 

    public function convert(array $data, $format)
    {
        foreach ($this->converters as $converter) {
            if ($converter->supports($format)) {
                return $converter->convert($data);
            }
        }

        throw new \RuntimeException('No supported Converters found in chain.');
    }
}
4

2 回答 2

3

Symfony 4.2.4上带有标记服务编译器通道的工作示例:

\应用\服务\策略\文件上下文:

declare(strict_types=1);

namespace App\Service\Strategy;

class FileContext
{
    /**
     * @var array
     */
    private $strategies;

    /**
     * @param FileStrategyInterface $strategy
     */
    public function addStrategy(FileStrategyInterface $strategy): void
    {
        $this->strategies[] = $strategy;
    }

    /**
     * @param string $type
     *
     * @return string
     */
    public function read(string $type): string
    {
        /** @var FileStrategyInterface $strategy */
        foreach ($this->strategies as $strategy) {
            if ($strategy->isReadable($type)) {
                return $strategy->read();
            }
        }

        throw new \InvalidArgumentException('File type not found');
    }
}

\App\Service\Strategy\FileStrategyInterface:

declare(strict_types=1);

namespace App\Service\Strategy;

interface FileStrategyInterface
{
    /**
     * @param string $type
     *
     * @return bool
     */
    public function isReadable(string $type): bool;

    /**
     * @return string
     */
    public function read(): string;
}

\App\Service\Strategy\CsvStrategy:

declare(strict_types=1);

namespace App\Service\Strategy;

class CsvStrategy implements FileStrategyInterface
{
    private const TYPE = 'csv';

    /**
     * {@inheritdoc}
     */
    public function isReadable(string $type): bool
    {
        return self::TYPE === $type;
    }

    /**
     * {@inheritdoc}
     */
    public function read(): string
    {
        return 'Read CSV file';
    }
}

\应用\服务\策略\文本策略:

class TxtStrategy implements FileStrategyInterface
{
    private const TYPE = 'txt';

    /**
     * {@inheritdoc}
     */
    public function isReadable(string $type): bool
    {
        return self::TYPE === $type;
    }

    /**
     * {@inheritdoc}
     */
    public function read(): string
    {
        return 'Read TXT file';
    }
}

\App\DependencyInjection\Compiler\FileContextCompilerPass:

declare(strict_types=1);

namespace App\DependencyInjection\Compiler;

use App\Service\Strategy\FileContext;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class FileContextCompilerPass implements CompilerPassInterface
{
    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container)
    {
        $service = $container->findDefinition(FileContext::class);

        $strategyServiceIds = array_keys($container->findTaggedServiceIds('strategy_file'));

        foreach ($strategyServiceIds as $strategyServiceId) {
            $service->addMethodCall('addStrategy', [new Reference($strategyServiceId)]);
        }
    }
}

\应用\内核:

namespace App;

use App\DependencyInjection\Compiler\FileContextCompilerPass;
...

class Kernel extends BaseKernel
{
    use MicroKernelTrait;
...
    /**
     * {@inheritdoc}
     */
    protected function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new FileContextCompilerPass());
    }
}

确保您已添加有效的标记服务定义。

配置/服务.yaml:

    App\Service\Strategy\TxtStrategy:
        tags:
            - { name: strategy_file }

    App\Service\Strategy\CsvStrategy:
        tags:
            - { name: strategy_file }

用于测试的命令。 \应用\命令\策略命令:

declare(strict_types=1);

namespace App\Command;

use App\Service\Strategy\FileContext;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class StrategyCommand extends Command
{
    /**
     * {@inheritdoc}
     */
    protected static $defaultName = 'strategy:run';

    /**
     * @var FileContext
     */
    private $fileContext;

    /**
     * @param FileContext $fileContext
     */
    public function __construct(FileContext $fileContext)
    {
        $this->fileContext = $fileContext;

        parent::__construct();
    }

    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setDescription('Run strategy example')
            ->addOption('file', null, InputOption::VALUE_REQUIRED);
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        if (!$file = trim($input->getOption('file'))) {
            return;
        }

        echo $this->fileContext->read($file);
    }
}

结果:

$ bin/console strategy:run --file=txt
Read TXT file%
$ bin/console strategy:run --file=csv
Read CSV file%
于 2019-03-30T10:37:34.277 回答
0

我的问题出在ConverterPass类中,而不是services.yaml中的服务 ID,我的类名可以正常工作。

于 2019-04-02T09:31:25.350 回答