【问题标题】:How can I inject service with setter injection in my Symfony2 extension?如何在 Symfony2 扩展中使用 setter 注入来注入服务?
【发布时间】:2015-02-18 18:55:47
【问题描述】:

我在捆绑扩展中定义了一些配置:

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 注入中从容器中获取服务?

【问题讨论】:

标签: php symfony dependency-injection


【解决方案1】:

我找到了解决方案。我可以测试我的$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))
        );
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-16
    • 2017-01-06
    相关资源
    最近更新 更多