如何在我的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中受益的服务。但现在我需要重写MyExtensionMyService以满足新的要求。

如何从以这种方式调用的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))
        );
    }
}