【问题标题】:Symfony2 dependency injection container sub-containerSymfony2 依赖注入容器子容器
【发布时间】:2013-05-24 06:56:39
【问题描述】:

是否可以将一些服务放入另一个容器中,该容器将缩小到特定的一组服务?或者将一些特定的服务分离到某种子容器中?我问这个问题是因为我需要将一组服务注入到另一个服务中,而将整个容器放入服务中,我相信,这是个坏主意。当然,我可以在我的服务类中为我想要注入的所有服务创建一个属性,但这是我试图避免的解决方案。

如果您需要有关该问题的更多信息,我将不胜感激。

【问题讨论】:

    标签: symfony service dependency-injection


    【解决方案1】:

    基本上,您可以通过编写一些带有特殊名称的“标记”服务来使用 DIC。
    为此,您需要将您的服务定义到一个文件中(遵循 DIC 规范)并以特定方式标记它们(在这种情况下,代码将由 Sonata Admin Bundle 获取,用于解释)

    # MyBundle/Resources/config/admin.yml
    services:
        sonata.admin.tag:
            class: YourNS\AdminBundle\Admin\BlogAdmin
            tags:
                - { name: sonata.admin, manager_type: orm, group: posts, label: "Blog" }
            arguments:
                - ~
                - YourNS\AdminBundle\Entity\Course
                - 'SonataAdminBundle:CRUD'
            calls:
                - [ setTranslationDomain, [YourNSAdminBundle]]
    

    在这种情况下,我定义了一个名为sonata.admin.tag 的服务,该服务带有sonata.admin 标签。
    我可以用sonata.admin.tag标签名定义十几个。

    完成此操作后,我必须创建一个“特殊”文件(我将把它放入 bundle 的 DependencyInjection 文件夹 [用于约定]),即 CompilerPass file
    什么是 CompilerPass 文件?

    编译器通行证让您有机会操纵其他服务 已在服务容器中注册的定义。[...] 编译器传递最常见的用例之一是使用 标记的服务(在组件部分阅读有关标记的更多信息 “使用标记服务”)。

    这正是你所需要的!

    现在您必须搜索(在此文件中)标记为(在此特定示例中)sonata.admin 的服务

    class AddDependencyCallsCompilerPass implements CompilerPassInterface
    {
      /**
      * {@inheritDoc}
      */
        public function process(ContainerBuilder $container)
        {
            $groupDefaults = $admins = $classes = array();
    
            $pool = $container->getDefinition('sonata.admin.pool');
    
            foreach ($container->findTaggedServiceIds('sonata.admin') as $id => $tags) {
                foreach ($tags as $attributes) {
                    $definition = $container->getDefinition($id);
    
                    $arguments = $definition->getArguments();
    
                    if (strlen($arguments[0]) == 0) {
                        $definition->replaceArgument(0, $id);
                    }
    
                    if (strlen($arguments[2]) == 0) {
                        $definition->replaceArgument(2, 'SonataAdminBundle:CRUD');
                    }
    
                    $this->applyConfigurationFromAttribute($definition, $attributes);
                    $this->applyDefaults($container, $id, $attributes);
    
                    $arguments = $definition->getArguments();
    
                    $admins[] = $id;
                    //other logic here
                    $pool->addMethodCall('setAdminClasses', array($classes));
    

    正如您在此处看到的,我们正在搜索标记为 sonata.admin ($container->findTaggedServiceIds('sonata.admin')) 的服务,并将这些服务(在这种情况下,专门针对奏鸣曲管理包)添加到 $pool,即 @ 987654333@

    现在,我们必须将 CompilerPass 注册到我们的包文件中(您在将包注册到应用程序之前创建的那个)

    class SonataAdminBundle extends Bundle
    {
        /**
        * {@inheritDoc}
        */
        public function build(ContainerBuilder $container)
        {
            $container->addCompilerPass(new AddDependencyCallsCompilerPass());
        }
    }
    

    现在,您已经为这个捆绑包注册了某种服务。服务工厂更适合

    Symfony2 的服务容器提供了一种强大的控制方式 对象的创建,允许您指定传递给的参数 构造函数以及调用方法和设置参数。 但是,有时这并不能为您提供所需的一切 来构造你的对象。

    或将其视为服务实例化的“入口点”。

    【讨论】:

    • 哇,感谢您的努力。这对我来说是全新的方法。我必须阅读有关 CompilerPass 的信息。但我的问题是:这种方法是否为我提供了一个带有标记服务的单独容器?如果是,我该如何访问它?
    • @pawel.kalisz: 为什么你想要一个单独的容器???我想这是不可能的。阅读您的问题,我看到“当然,我可以在我的服务类中为我想要注入的所有服务创建一个属性,但这是我试图避免的解决方案。”这将避免“手工”编写所有服务:)
    【解决方案2】:

    出于性能和可测试性的原因,确实不推荐注入整个容器。

    如果所有依赖项的构造函数或 getter/setter 注入不是您想要的方式......

    ...Service Factory 就是您要查找的内容。

    通过这种方式,您可以构建一个包含其他服务的服务并只注入这个服务。

    这就是你所说的“子容器”。

    【讨论】:

    • 是您在寻找的东西吗?
    • 部分是的,我可以实现我想要的,但不是以我想要的方式;)。我的意思是多亏了您的解决方案,我可以创建一个具有大量方法的工厂,例如“getServiceOne”、“getServiceTwo”,甚至可以使用 get('service.one') 等模拟容器行为。我不能做的是使用容器在工厂创建服务。我的意思是我必须在工厂方法中手动提供所有依赖项,这没关系,这个解决方案是我的问题目标之前的一步。考虑到所有事情,您的解决方案是令人满意的,我会使用它,但我仍然会使用第二个(有限)容器实例。THX!
    • 但您是否也不必手动向第二个容器注册服务? :)
    • 嗯,这是一个有趣的问题。我认为最好的解决方案是使用我感兴趣的服务创建一个单独的 services.yml,并告诉依赖注入机制将这个单独的文件加载到另一个容器中。这就是我正在寻找的,我不知道它是否可能。但恐怕不是,因为这种方法需要symfony一次提供多个容器,据我所知这是不可能的,是吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-15
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 2018-12-18
    相关资源
    最近更新 更多