Symfony - получить сервис по имени класса из итерируемых - внедренных тегированных сервисов - PullRequest
0 голосов
/ 01 марта 2019

Я изо всех сил пытаюсь получить конкретную услугу через имя класса из группы внедренных помеченных служб.

Вот пример: я отмечаю все службы, которые реализуют DriverInterface 'app.driver', и привязываю его к $Переменная drivers.

В каком-то другом сервисе мне нужно получить все те драйверы, которые помечены как app.driver, создать их экземпляр и использовать только несколько из них.Но то, что нужно, будет динамически.

services.yml

_defaults:
        autowire: true
        autoconfigure: true
        public: false
        bind:
            $drivers: [!tagged app.driver]

_instanceof:
        DriverInterface:
            tags: ['app.driver']

некоторые другие службы:

/**
 * @var iterable
 */
private $drivers;

/**
 * @param iterable $drivers
 */
public function __construct(iterable $drivers) 
{
    $this->drivers = $drivers;
}

public function getDriverByClassName(string $className): DriverInterface
{
    ????????
}

Таким образом, службы, реализующие интерфейс драйвера, вводятся в $Это-> драйверы параметр как итеративный результат.Я могу только проповедовать через них, но тогда будут созданы все службы.

Есть ли какой-то другой способ внедрить эти службы, чтобы получить от них определенную службу через имя класса, не создавая экземпляры других?

PS Я знаю, что есть возможность сделать эти драйверы общедоступными и использовать вместо них контейнеры, но я бы хотел избежать внедрения в сервисы Cotaintainer, если это возможно.

1 Ответ

0 голосов
/ 01 марта 2019

A ServiceLocator позволит получить доступ к службе по имени, не создавая экземпляры остальных из них.Это требует прохождения компилятора, но его не слишком сложно настроить.

use Symfony\Component\DependencyInjection\ServiceLocator;
class DriverLocator extends ServiceLocator
{
    // Leave empty
}
# Some Service
public function __construct(DriverLocator $driverLocator) 
{
    $this->driverLocator = $driverLocator;
}

public function getDriverByClassName(string $className): DriverInterface
{
    return $this->driverLocator->get($fullyQualifiedClassName);
}

Теперь приходит волшебство:

# src/Kernel.php
# Make your kernel a compiler pass
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface {
...
# Dynamically add all drivers to the locator using a compiler pass
public function process(ContainerBuilder $container)
{
    $driverIds = [];
    foreach ($container->findTaggedServiceIds('app.driver') as $id => $tags) {
        $driverIds[$id] = new Reference($id);
    }
    $driverLocator = $container->getDefinition(DriverLocator::class);
    $driverLocator->setArguments([$driverIds]);
}

И presto.Это должно сработать, если вы исправите любые синтаксические ошибки или опечатки, которые я мог ввести.

И для дополнительной поддержки вы можете автоматически зарегистрировать свои классы драйверов и избавиться от этого экземпляра записи в файле служб.

# Kernel.php
protected function build(ContainerBuilder $container)
{
    $container->registerForAutoconfiguration(DriverInterface::class)
        ->addTag('app.driver');
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...