【问题标题】:How to get Symfony 4 dependency injection working with two different use case scenarios?如何让 Symfony 4 依赖注入与两个不同的用例场景一起工作?
【发布时间】:2020-02-26 20:50:50
【问题描述】:

我们正在努力寻找在一个有相当具体问题的 Symfony 项目中实现依赖注入的最佳方法。

在用户级别,我们的应用程序依赖于一个“帐户”原则实体,该实体在 HTTP_HOST 全局属性的帮助下加载到域属性(多域应用程序)。继续域 example.domain.tld 将加载匹配的实体和设置。

在 devops 级别,我们还需要同时在多个帐户上使用 CLI 脚本进行批处理。

我们面临的问题是如何编写能够同时满足这两种需求的服务?

让我们用一个简化的例子来说明这一点。对于用户级别,我们有这个,一切都很好:

控制器/文件控制器.php

    public function new(Request $request, FileManager $fileManager): Response
    {
        ...
        $fileManager->addFile($file);
        ...

    }

服务/FileManager.php

    public function __construct(AccountFactory $account)
    {
        $this->account = $account;
    }

服务/AccountFactory.php

    public function __construct(RequestStack $requestStack, AccountRepository $accountRepository)
    {
        $this->requestStack = $requestStack;
        $this->accountRepository = $accountRepository;
    }

    public function createAccount()
    {
        $httpHost = $this->requestStack->getCurrentRequest()->server->get('HTTP_HOST');
        $account = $this->accountRepository->findOneBy(['domain' => $httpHost]);

        if (!$account) {
            throw $this->createNotFoundException(sprintf('No matching account for given host %s', $httpHost));
        }

        return $account;
    }

现在如果我们想编写以下控制台命令,它会失败,因为 FileManager 只接受 AccountFactory 而不是帐户实体。

$accounts = $accountRepository->findAll();
foreach ($accounts as $account) {
    $fileManager = new FileManager($account);
    $fileManager->addFile($file);
}

我们可以调整 AccountFactory 但这会感觉不对... 实际上,这甚至更糟,因为 Account 依赖于服务更深。

有人知道如何正确地做到这一点吗?

【问题讨论】:

  • 引入一个通用接口怎么样?
  • @NicoHaase 你将如何创建一个教义实体的通用接口?
  • 我不明白这个问题 - Doctrine 实体和所有其他实体一样是 PHP 类
  • @NicoHaase 那我不明白你所说的通用接口是什么意思:)
  • 您编辑实体类,并在它显示class EntityXY 的那一行中追加class EntityXY implements CommonInterface - 现在,公共类可以依赖该接口,并且两个更改类都实现了公共接口所有常用方法

标签: php symfony doctrine-orm symfony4


【解决方案1】:

作为一个好的做法,您应该为 FileManager 创建一个接口并将这个 FileManagerInterface 设置为您的依赖注入(而不是 FileManager)。 然后,您可以拥有遵循相同接口规则但具有不同构造函数的不同类。

通过这种方法,您可以实现类似:

服务/FileManager.php

interface FileManagerInterface
{
    // declare the methods that must be implemented
    public function FileManagerFunctionA();
    public function FileManagerFunctionB(ParamType $paramX):ReturnType;
}

文件管理器接口.php

class FileManagerBase implements FileManagerInterface
{
    // implement the methods defined on the interface
    public function FileManagerFunctionA()
    {
        //... code
    }

    public function FileManagerFunctionB(ParamType $paramX):ReturnType
    {
        //... code
    }
}

FileManagerForFactory.php

class FileManagerForFactory implements FileManagerInterface
{
    // implement the specific constructor for this implementation
    public function __construct(AccountFactory $account)
    {
        // your code here using the account factory object
    }
    // additional code that is needed for this implementation and that is not on the base class
}

FileManagerAnother.php

class FileManagerForFactory implements FileManagerInterface
{
    // implement the specific constructor for this implementation
    public function __construct(AccountInterface $account)
    {
        // your code here using the account object
    }
    // additional code that is needed for this implementation and that is not on the base class
}

最后但同样重要的是:

控制器/文件控制器.php

public function new(Request $request, FileManagerInterface $fileManager): Response
{
    // ... code using the file manager interface
}

另一种看起来也正确的方法是,假设 FileManager 依赖于 AccountInstance 工作,可以对您的 FileManager 依赖项进行更改,以将 AccountInstance 作为依赖项而不是 Factory .只是因为实际上FileManager不需要工厂,它需要工厂生成的结果,所以,自动承载整个工厂不是FileManager的责任。

使用这种方法,您只需更改您的声明,例如:

服务/FileManager.php

public function __construct(AccountInterface $account)
{
    $this->account = $account;
} 

服务/AccountFactory.php

public function createAccount():AccountInterface
{
    // ... your code
}

【讨论】:

  • 有趣的方法。您能否更详细地解释使用 FileManager 接口将如何解决问题中提出的 Account/AccountFactory 困境?
  • 这种方法似乎很繁重,因为 FileManager 类将共享完全相同的代码,除了结构。此外,FileManager 不是唯一关心此问题的服务。许多其他的也依赖于这个逻辑。我们没有看到自己复制了项目中的所有类。但也许这是唯一干净的解决方案......
  • @Cerad 这解决了 Account/AccountFactory 的困境,因为两个不同的类实现了相同的接口。一个将被调用用于用户级别,另一个用于批处理操作。但这似乎很重...
  • 是的,这不是一个简单的实现,但它看起来是一个更好的方法。我对答案做了额外的评论,这也可能是一个很好的解决方案。
  • @MarcelKohls 我不认为您的附加答案是可能的,因为它是一个学说实体。是否可以将学说实体注入服务?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-05
  • 2021-04-14
  • 2019-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多