【问题标题】:How can I access a service outside of a controller with Symfony2?如何使用 Symfony2 访问控制器之外的服务?
【发布时间】:2011-09-01 17:44:32
【问题描述】:

我正在构建一个非常依赖第三方 API 的网站,因此我认为将 API 包装器打包为服务是有意义的,但是我开始寻找使用它的实例在控制器之外访问它,例如在实体存储库中。 与此相关的还有,能够访问控制器之外的配置值(例如在实体存储库中)将很有用。

谁能告诉我这是否可行,如果不可行,是否有建议的方法来做这种事情?

感谢您的帮助

【问题讨论】:

    标签: symfony service


    【解决方案1】:

    Symfony 发行版严重依赖依赖注入。这意味着通常,依赖项通过构造函数、设置器或其他方式(如属性反射)直接注入到您的对象中。然后,您的 API 包装服务成为您应用程序其他对象的依赖项。

    话虽如此,在实体存储库的构造函数中注入此服务是相当困难的,因为它已经需要一些其他参数,而且我认为由于我们为实体请求存储库的方式而无法注入它们.

    您可以做的是创建另一个服务,该服务将负责执行您将在实体存储库中执行的工作。这样,您将能够注入实体管理器,该管理器将用于检索实体存储库、您的自定义服务以及另一个保存您的配置值的服务(还有其他方法可以共享配置值)。

    在我的用例中,我使用了一个包装 Facebook API 调用的 Facebook 帮助服务。然后将此服务注入我需要的地方。我的实体存储库只负责进行数据库调用,因此它只接收它需要的参数而不是整个依赖项。因此,它不会接收帮助程序,而只会接收执行请求所需的参数,例如 Facebook 用户 ID。在我看来,这是这样做的方法,因为我认为实体存储库不应该依赖于此类帮助对象。

    这里是一个使用 YAML 作为配置的小例子:

    # app/config/config.yml
    services:
      yourapp.configuration_container:
        class: Application/AcmeBundle/Common/ConfigurationContainer
        # You could inject configurations here      
    
      yourapp.api_wrapper:
        class: Application/AcmeBundle/Service/ApiWrapperService
        # Inject other arguments if needed and update constructor in consequence    
    
      yourapp.data_access:
        class: Application/AcmeBundle/Data/Access/DatabaseAccessService
        arguments: 
          entityManager: "@doctrine.orm.entity_manager"
          apiWrapperService: "@yourapp.api_wrapper"
          configuration: "@yourapp.configuration_container"
    
    # Application/AcmeBundle/Common/ConfigurationContainer.php
    public ConfigurationContainer
    {
       public function __construct()
       {
           // Initialize your configuration values or inject them in the constructor
       }
    }        
    
    # Application/AcmeBundle/Service/ApiWrapperService.php
    public ApiWrapperService
    {
       public function __construct()
       {
           // Do some stuff
       }
    }
    
    # Application/AcmeBundle/Data/Access/DatabaseAccessService.php
    public DatabaseAccessService
    {
        public function __construct(EntityManager $entityManager, ApiWrapperService $apiWrapperService, ConfigurationContainer $configuration)
        {
            ...
        }
    }
    

    config.yml 文件中的 at 符号 (@) 表示 Symfony 应该注入另一个服务,其 id 定义在 at 符号之后,而不是简单的字符串。对于配置值,正如我之前所说,还有其他方法可以实现相同的目标,例如使用参数或捆绑扩展。使用 bundle 扩展,您可以将配置值直接定义到 config.yml 中,并且您的 bundle 会读取它们。

    总之,这应该让您大致了解注入服务。这里是有关该主题的文档的小列表。许多链接使用 XML 服务定义而不是 YAML 定义,但您应该能够很容易地理解它们。

    1. Symfony Official DI
    2. Fabien Potencier's articles on DI
    3. Richard Miller's articles on DI(在他的博客中查看其他 DI 文章)

    请注意,我提供的配置适用于 Symfony2 的 Beta1。我还没有更新到 Beta2,所以可能有一些东西在 Beta2 版本中无法正常工作。

    我希望这将帮助您确定问题的最终解决方案。如果您需要澄清或其他任何问题,请随时提出其他问题。

    问候, 马特

    【讨论】:

    • 这是巨大的帮助,我已经将我的 API 服务注入到我的数据库服务中,这很棒我仍然不清楚如何最好地进行配置。如果我解释我正在尝试做的事情可能是最好的。我正在构建一个可以跨多个域工作的站点,每个站点都将作为具有自己的 yaml 配置文件的环境存在。在配置文件中将是一个站点 id,例如,我想要的是能够调用 getUsers() 并让它从配置中获取站点 id 并自动将“where site_id = x”添加到查询中。这是可能的还是我应该将 id 作为参数传递?
    • 刚刚意识到您链接到的 Symfony 文章解释了如何执行此操作,再次感谢您的帮助。这几天让我感到压力很大,所以很高兴有一个解决方案。
    • 很高兴它有所帮助,并且您现在有了解决问题的方法 :) 事实上,我链接的文章更明确地说明了如何使用参数和类似的东西。祝你工作顺利。
    • 小心复制代码!它包含__constructor 而不是__construct,顺便说一下答案+1!
    【解决方案2】:

    我会将这种行为封装在 Symfony 服务中(如管理器)。 我不会将任何参数或逻辑注入实体存储库,因为它们应该主要用于使用对象管理器查询获取数据。 我会将逻辑放在服务中,如果服务需要访问数据库,它将调用实体存储库来获取数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-19
      • 1970-01-01
      相关资源
      最近更新 更多