A Symfony Сервисный локатор может использоваться, чтобы избежать необходимости вводить весь контейнер.Локатор действует как контейнер, но имеет доступ только к ограниченному количеству сервисов.
Требуется немного волшебства, чтобы настроить все.В вашем случае вам нужен только локатор для доступа к сервисам, реализующим интерфейс Doing.
Начните с локатора, который будет наследовать методы get / has, как и все контейнеры:
use Symfony\Component\DependencyInjection\ServiceLocator;
class DoingLocator extends ServiceLocator
{
protected $services = [
'bar' => 'app.service.bar',
'baz' => 'app.service.baz'
];
public function locate($string) {
return $this->get($this->services[$string]);
}
}
Теперь приходитмагия.На самом деле вы можете настроить это вручную в services.yaml согласно документации, но более интересно сделать это автоматически.
Начните с того, что ваш класс Kernel будет проходить компилятор:
# src/Kernel.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface
Далее,автоматически помечать все сервисы, которые реализуют интерфейс Doing:
# Kernel.php
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(Doing::class)
->addTag('doing');
}
Наконец, добавьте проход компилятора для построения вашей службы локатора:
# Kernel.php
public function process(ContainerBuilder $container)
{
$doingLocatorIds = [];
foreach ($container->findTaggedServiceIds('doing') as $id => $tags) {
$doingLocatorIds[$id] = new Reference($id);
}
$doingLocator = $container->getDefinition(DoingLocator::class);
$doingLocator->setArguments([$doingLocatorIds]);
}
И presto.Вы сделали.Теперь вы можете ввести свой DoingLocator (он же MyServiceFactory), и все должно быть хорошо.