如何让Symfony 4依赖项注入在两种不同的用例场景中工作



我们正试图找到在Symfony项目中实现依赖项注入的最佳方法,该项目有一个非常具体的问题。

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

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

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

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

控制器/FileController.php

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

Service/FileManager.php

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

Service/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,而不接受Account实体。

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

我们可以在AccountFactory中进行调整,但这感觉不对。。。事实上,情况更糟,因为Account在服务中的依赖性更深。

有人知道如何正确制作吗?

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

使用这种方法,您可以实现以下内容:

Service/FileManager.php

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

FileManager界面.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
}

文件管理器Another.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
}

最后但同样重要的是:

控制器/FileController.php

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

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

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

Service/FileManager.php

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

Service/AccountFactory.php

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

相关内容

  • 没有找到相关文章

最新更新