在不向子类传递参数的情况下共享Symfony服务中的依赖项



我的许多服务依赖于到外部数据库的PDO连接(由于我的应用程序特定的原因,使用这种策略比使用Doctrine更有意义)。要启动PDO连接,每个服务都需要数据源名称、用户名和密码。这使得我的services.yml包含了每个服务的许多相同的参数:

#AppBundleResourcesconfigservices.yml
    # ...
    QueryDataBuilderHelper:
        class: AppBundleServicesQueryDataBuilderHelper
        arguments: [ "%database_host%", "%database_user%", "%database_password%" ]
    ZipCodeClass:
        class: AppBundleServicesZipCodeClass
        arguments: [ "%database_host%", "%database_user%", "%database_password%" ]
    # ...

是否有可能在某处定义连接并在所有服务中引用它而不向每个服务传递参数?

Symfony指南建议使用父服务并扩展父服务。当我尝试使用子类时,子类不提取超类的参数:

#AppBundleResourcesconfigservices.yml
DBConnectionHelper:
    class: AppBundleServicesDBConnectionHelper
    arguments: [ "%database_host%", "%database_user%", "%database_password%" ]

DBSubClass:
    class: AppBundleServicesDBSubClass
    parent: DBConnectionHelper
    arguments: [ "%unrelated_parameter%" ] 

//AppBundleServicesSuperClassService.php
namespace AppBundleServices;
class DBConnectionHelper
{
    public function __construct($dsn, $user, $password){
        $this->DB_connection = new PDO($dsn, $user, $password);
    }
}

//AppBundleServicesDBSubClass.php
namespace AppBundleServices;
class DBSubClass extends DBConnectionHelper
{
    public function __construct($unrelated_param){
        //Calling parent::__construct() here will require the parameters again. 
        //Which is what I am trying to avoid...
        // Outputs a notice that DB_Connection isn't set.
        var_dump($this->DB_Connection());
        $this->unrelated_param = $unrelated_param;
    }
}

我在这里使用构造函数而不是setter,因为这些依赖关系不是可选的。Symfony文档建议,只有当依赖项是可选的时候,才应该使用setter。

这里需要使用的称为Abstract Service。幸运的是,Symfony提供了一种实现这一点的方法。您正在寻找的选项是abstractparent。你可以在这里阅读整章。

我将试着从文档中简要地解释这个例子,以便你能理解其中的意思。

# ...
services:
    # ...
    mail_manager:
        abstract:  true
        calls:
            - [setMailer, ["@my_mailer"]]
            - [setEmailFormatter, ["@my_email_formatter"]]
    newsletter_manager:
        class:  "NewsletterManager"
        parent: mail_manager
    greeting_card_manager:
        class:  "GreetingCardManager"
        parent: mail_manager

基本上你需要做的是:

  • 创建一个抽象类,在其中定义所有常用的属性/方法。
  • 然后在您的服务文件中相应地配置这些常用方法调用,并添加abstract选项。因为你的服务是一个abstract,这意味着它不能被实例化,所以class选项在这里被省略。之后,像往常一样创建一个服务,不要忘记extend抽象类。然后通过设置抽象服务的名称,在service.yml文件中注册该服务并添加parent选项。
  • 为每个子服务重复上面的步骤,希望重复多少次就可以了。
如果你有任何问题,请留下评论。

与其扩展DBConnectionHelper类,不如通过构造函数将其作为依赖项注入。

//AppBundleServicesDBSubClass.php
namespace AppBundleServices;
class DBSubClass
{
    private $dbConnectionHelper;
    public function __construct($dbConnectionHelper, $unrelated_param)
    {
        $this->dbConnectionHelper = $dbConnectionHelper;
        $this->unrelated_param = $unrelated_param;
    }
}

你可以使用$this->dbConnectionHelper->DB_Connection()获得连接。

您还需要将您的服务配置为:

DBSubClass:
    class: AppBundleServicesDBSubClass
    arguments: [ "@DBConnectionHelper", "%unrelated_parameter%" ] 

最新更新