p.m*_*mar 5 php autowired symfony symfony-dependency-injection
我正在努力通过类别名称从注入的已标记服务组中获取特定服务。
这是一个示例:我标记实现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']
Run Code Online (Sandbox Code Playgroud)
其他一些服务:
/**
* @var iterable
*/
private $drivers;
/**
* @param iterable $drivers
*/
public function __construct(iterable $drivers)
{
$this->drivers = $drivers;
}
public function getDriverByClassName(string $className): DriverInterface
{
????????
}
Run Code Online (Sandbox Code Playgroud)
因此,将实现的服务DriverInterface注入到$this->driversparam中作为可迭代的结果。我只能foreach通过它们,但是所有服务都将被实例化。
是否有其他方法可以将这些服务注入,以通过类名从它们获得特定服务而无需实例化其他服务?
我知道有可能将那些驱动程序公开并改为使用容器,但是我想避免以其他方式将容器注入服务中。
yiv*_*ivi 10
您不再(自 Symfony 4 起)需要创建编译器通道来配置服务定位器。
可以通过配置做任何事情,让 Symfony 发挥“魔力”。
您可以在配置中添加以下内容:
services:
_instanceof:
DriverInterface:
tags: ['app.driver']
lazy: true
DriverConsumer:
arguments:
- !tagged_locator
tag: 'app.driver'
Run Code Online (Sandbox Code Playgroud)
需要访问这些而不是接收 的服务iterable接收ServiceLocatorInterface:
class DriverConsumer
{
private $drivers;
public function __construct(ServiceLocatorInterface $locator)
{
$this->locator = $locator;
}
public function foo() {
$driver = $this->locator->get(Driver::class);
// where Driver is a concrete implementation of DriverInterface
}
}
Run Code Online (Sandbox Code Playgroud)
而就是这样。你不需要其他任何东西,它只是有效tm。
包含所有类的完整示例。
我们有:
FooInterface:class DriverConsumer
{
private $drivers;
public function __construct(ServiceLocatorInterface $locator)
{
$this->locator = $locator;
}
public function foo() {
$driver = $this->locator->get(Driver::class);
// where Driver is a concrete implementation of DriverInterface
}
}
Run Code Online (Sandbox Code Playgroud)
AbstractFoo为了简化实现,我们将在具体服务中扩展一个抽象类:
abstract class AbstractFoo implements FooInterface
{
public function whoAmI(): string {
return get_class($this);
}
}
Run Code Online (Sandbox Code Playgroud)
几个实现的服务 FooInterface
class FooOneService extends AbstractFoo { }
class FooTwoService extends AbstractFoo { }
Run Code Online (Sandbox Code Playgroud)
另一个需要服务定位器来使用我们刚刚定义的这两个服务的服务:
class Bar
{
/**
* @var \Symfony\Component\DependencyInjection\ServiceLocator
*/
private $service_locator;
public function __construct(ServiceLocator $service_locator) {
$this->service_locator = $service_locator;
}
public function handle(): string {
/** @var \App\Test\FooInterface $service */
$service = $this->service_locator->get(FooOneService::class);
return $service->whoAmI();
}
}
Run Code Online (Sandbox Code Playgroud)
唯一需要的配置是:
interface FooInterface
{
public function whoAmI(): string;
}
Run Code Online (Sandbox Code Playgroud)
如果您想定义自己的服务名称而不是使用类名称,则可以使用静态方法来定义服务名称。配置将更改为:
App\Test\Bar:
arguments:
- !tagged_locator
tag: 'test_foo_tag'
default_index_method: 'fooIndex'
Run Code Online (Sandbox Code Playgroud)
其中fooIndex是在每个返回字符串的服务上定义的公共静态方法。注意:如果您使用此方法,您将无法通过类名获取服务。
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);
}
Run Code Online (Sandbox Code Playgroud)
现在魔法来了:
# 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]);
}
Run Code Online (Sandbox Code Playgroud)
和急速。假设您修复了我可能引入的任何语法错误或拼写错误,它应该可以工作。
为了获得额外的积分,您可以自动注册您的驱动程序类并删除服务文件中的该实例条目。
# Kernel.php
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(DriverInterface::class)
->addTag('app.driver');
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
285 次 |
| 最近记录: |