Symfony2,自动加载服务

时间:2013-05-19 07:51:22

标签: symfony service autoloader

问题很简单但...... 所以我们有主要服务:

class ManagerOne {}

并且我们想要在主服务中使用其他几项服务:

class ServiceOne{}
class ServiceTwo{}
class ServiceThree{}
class ServiceFour{}
...

每个都命名为(在services.yml中)

service.one
service.two
service.three
service.four
...

服务的位置不同,不在一个文件夹中(但我不认为这对自定义自动加载器来说是一个很大的麻烦)。

关于手册,我们可以通过主服务(ManagerOne)中的__construct()注入它们,但是如果我们需要注入20个这样的服务呢?或者只使用我们需要的东西。在服务中将它们描述为简单注入? O.o我认为这不是一个好主意....我们也可以注入容器,就是这样。但!到处都有人说注入容器最差的解决方案。

我想要什么。我需要ManagerOne服务的方法,它将通过'service.name'或'path'加载我需要的服务,并且检查'服务存在'。

2 个答案:

答案 0 :(得分:4)

您可以使用service tagging并标记要在ManagerOne课程中使用的每项服务。并使用构造函数依赖注入或方法注入。

示例:

首先,您需要编译器传递来收集标记服务:

namespace ...\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class ExamplePass implements CompilerPassInterface
{

    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition("manager.one")) {
            return;
        }
        $services = array();
        foreach ($container->findTaggedServiceIds('managed_service') as $serviceId => $tag) {
            $alias = isset($tag[0]['alias'])
                ? $tag[0]['alias']
                : $serviceId;

            // Flip, because we want tag aliases (= type identifiers) as keys
            $services[$alias] = new Reference($serviceId);
        }
        $container->getDefinition('manager.one')->replaceArgument(0, $services);
    }
}

然后,您需要将编译器传递添加到bundle类:

namespace Example\ExampleBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use ...\DependencyInjection\Compiler\ExamplePass;

class ExampleBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        parent::build($container);
        $container->addCompilerPass(new ExamplePass());
    }
}

现在您可以使用您的服务:

# services.yml
manager.one:
    class: ManagerClass
    arguments:
        - [] # will be replaced by our compiler pass

services.one:
    class: ServiceOne
    tags:
        - { name: managed_service, alias: service_one }

services.two:
    class: ServiceTwo
    tags:
        - { name: managed_service, alias: service_two }

但请注意,如果您找到经理,将自动创建所有服务类。如果这是性能缺陷,您只能将服务ID(不是Reference)传递给管理类。添加@service_container作为第二个参数,并根据需要创建服务。

答案 1 :(得分:0)

自2017年起,Symfony 3.3 Symplify\PackageBuilder变得更加容易。

感谢此套餐,您可以:

  • 放置代码
  • 使用严格的字符串类型
  • 使用简单的5行CompilerPass

让我们来看看你的例子


假设您有

  • 1位经理 - UpdateManager班级
  • 许多更新程序 - 实现UpdaterInterface
  • 的类

1。使用PSR-4 autodiscovery

的服务配置
# app/config/services.yml
services:
    _defaults:
        autowire: true

    App\:
        resource: ../../src/App

2。收集编译器通行证

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symplify\PackageBuilder\Adapter\Symfony\DependencyInjection\DefinitionCollector;

final class CollectorCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $containerBuilder)
    {
        DefinitionCollector::loadCollectorWithType(
            $containerBuilder,
            UpdateManager::class,
            UpdaterInterface::class,
            'addUpdater'
        );
    }
}

它收集UpdaterInterface类型的所有服务 并通过addUpdater()方法将其添加到UpdateManager

3。在Bundle中注册编译器传递

namespace App;

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class UpdaterBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(new CollectorCompilerPass);
    }
}

这就是全部!

如何添加新的更新程序?

只需创建实现UpdaterInterface的类,它就会被加载到UpdateManager

  • 没有标记
  • 无人工服务注册
  • 没有无聊的工作

享受!