Реализация шаблона стратегии Symfony 4 с пропуском компилятора для меня не работает - PullRequest
0 голосов
/ 27 марта 2019

Я реализую шаблон стратегии, используя 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 я получил сборку защищенной функции (ContainerBuilder $ container) {$container-> addCompilerPass (новый 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.');
    }
}

Ответы [ 2 ]

1 голос
/ 30 марта 2019

Рабочий пример для Symfony 4.2.4 с службами с тегами и проход компилятора :

\ App \ Service \ Strategy\ FileContext:

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';
    }
}

\ App \ Service \ Strategy \ TxtStrategy:

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)]);
        }
    }
}

\ App \ Kernel:

namespace App;

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

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

Убедитесь, что вы добавили допустимые определения служб с тэгом.

config / services.yaml:

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

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

Команда для тестирования. \ App \ Command \ StrategyCommand:

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%
0 голосов
/ 02 апреля 2019

Моя проблема была в классе ConverterPass , когда вместо идентификатора службы из services.yaml у меня есть имя класса, оно отлично работает.

...