如何在我的 Symfony2 扩展中使用 setter 注入来注入服务?
How can I inject service with setter injection in my Symfony2 extension?
我在我的包扩展中定义了一些配置:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
if (!empty($config['my_config'])) {
$my_config = $config['my_config'];
if (is_array($my_config)) {
$service_definition = $container->getDefinition('my_service');
foreach ($my_config as $name => $class) {
$reflection_class = new \ReflectionClass($class);
if (!$reflection_class->implementsInterface('MyInterface')) {
throw new \Exception(
"'$class' must implement MyInterface'"
);
}
$service_definition->addMethodCall('addElement', array($name, $class));
}
}
}
}
然后在 MyService
我有:
public function addElement($name, $class_name) {
$this->elements[$name] = new $class_name();
}
所有这些元素都实现了 MyInterface
,一切都很好,直到我有了对其他服务或参数有一定依赖性的新元素。这些新的 类 可以定义为受益于 DI 的服务。但是现在我需要重写 MyExtension
和 MyService
以满足新的要求。
如何从以这种方式调用的 setter 注入中的容器获取服务?
我找到了解决办法。我可以测试我的 $class
变量:
- 如果它是 POPO 的类名,那么就像我已经做的那样,在 setter 注入中使用它的构造函数
- 如果它是服务名称,我可以通过将
$class
包装在 Symfony\Component\DependencyInjection\Reference
对象中来为我的 setter 注入提供服务对象。
在 MyExtension
我需要下一行:
$service_definition->addMethodCall('addElement', array($name, new Reference($class)));
并在 MyService
中:
public function addElement($name, $object) {
if (class_exist($object)) {
$this->elements[$name] = new $object();
}
elseif ($object instanceof MyInterface) {
$this->elements[$name] = $object;
} else {
// throw an exception
}
}
但是如果您没有很多 POPO,您可以将所有对象注册为服务,正如@Marino Di Clemente 所提到的,您可以只使用标记来获取可以为您完成工作的所有服务。
在这种情况下,您必须将所有对象定义为服务并在其配置中添加适当的标签。然后你需要在扩展中获取它并传递给 setter 注入。代码将与我的类似,但您需要获取标记服务而不是解析配置:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
$service_definition = $container->getDefinition('my_service');
$taggedServices = $container->findTaggedServiceIds(
'my_tag'
);
foreach ($taggedServices as $id => $tags) {
$service_definition->addMethodCall(
'addElement',
array(new Reference($id))
);
}
}
我在我的包扩展中定义了一些配置:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
if (!empty($config['my_config'])) {
$my_config = $config['my_config'];
if (is_array($my_config)) {
$service_definition = $container->getDefinition('my_service');
foreach ($my_config as $name => $class) {
$reflection_class = new \ReflectionClass($class);
if (!$reflection_class->implementsInterface('MyInterface')) {
throw new \Exception(
"'$class' must implement MyInterface'"
);
}
$service_definition->addMethodCall('addElement', array($name, $class));
}
}
}
}
然后在 MyService
我有:
public function addElement($name, $class_name) {
$this->elements[$name] = new $class_name();
}
所有这些元素都实现了 MyInterface
,一切都很好,直到我有了对其他服务或参数有一定依赖性的新元素。这些新的 类 可以定义为受益于 DI 的服务。但是现在我需要重写 MyExtension
和 MyService
以满足新的要求。
如何从以这种方式调用的 setter 注入中的容器获取服务?
我找到了解决办法。我可以测试我的 $class
变量:
- 如果它是 POPO 的类名,那么就像我已经做的那样,在 setter 注入中使用它的构造函数
- 如果它是服务名称,我可以通过将
$class
包装在Symfony\Component\DependencyInjection\Reference
对象中来为我的 setter 注入提供服务对象。
在 MyExtension
我需要下一行:
$service_definition->addMethodCall('addElement', array($name, new Reference($class)));
并在 MyService
中:
public function addElement($name, $object) {
if (class_exist($object)) {
$this->elements[$name] = new $object();
}
elseif ($object instanceof MyInterface) {
$this->elements[$name] = $object;
} else {
// throw an exception
}
}
但是如果您没有很多 POPO,您可以将所有对象注册为服务,正如@Marino Di Clemente 所提到的,您可以只使用标记来获取可以为您完成工作的所有服务。
在这种情况下,您必须将所有对象定义为服务并在其配置中添加适当的标签。然后你需要在扩展中获取它并传递给 setter 注入。代码将与我的类似,但您需要获取标记服务而不是解析配置:
public function load(array $configs, ContainerBuilder $container)
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
$service_definition = $container->getDefinition('my_service');
$taggedServices = $container->findTaggedServiceIds(
'my_tag'
);
foreach ($taggedServices as $id => $tags) {
$service_definition->addMethodCall(
'addElement',
array(new Reference($id))
);
}
}