在 Symfony 中基于动态值注入服务
Inject service based on dynamic value in Symfony
我有 2 个服务,BlueWorkerService
和 YellowWorkerService
,都实现相同的接口,WorkerServiceInterface
。这些服务中的每一个都使用相同的实体,但具有不同的所需逻辑。
我需要注入其中一个,而不是两个,类 并在 ProcessorService
中使用它们,以便在正确的 Worker 上调用接口方法。使用哪个 Worker 服务取决于当前正在处理的 Worker。我会分解它:
Class WorkerProcessor {
private $workerService;
public function __construct(WorkerServiceInterface $workerServiceInterface)
{
$this->workerService = $workerServiceInterface;
}
public function getMixedColourWithRed() {
return $this->workerService->mixWithRed();
}
}
正在使用的工作人员服务将基于正在处理的工作人员是否具有 Blue
或 Yellow
的 colour
属性。
我知道我可能可以使用 Factory 来实现这个 as described here 但我的问题是如何告诉工厂我正在处理哪种 Worker 颜色?
运行 在 Symfony 上 3.4
如果您需要更多信息,请提问,我会更新问题。
注意:我使用的是 Symfony 4.3.1。我会 post 那样做,然后我会帮助您将所有代码从该架构移至 Symfony 3.4。
我正在使用类似的概念在我的项目中加载不同的 classes。我先解释一下,然后我会在这段文字下添加代码。
首先,我在 src/Kernel.php
下加载自定义编译器传递(您的文件是 app/AppKernel.php
):
/**
* {@inheritDoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new BannerManagerPass());
}
BannerManagerPass
它是在 src/DependencyInjection/Compiler
下创建的(在您的情况下应该是 src/BUNDLE/DependencyInjection/Compiler`)。
class BannerManagerPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(BannerManager::class)) {
return;
}
$definition = $container->findDefinition(BannerManager::class);
$taggedServices = $container->findTaggedServiceIds('banner.process_banners');
foreach (array_keys($taggedServices) as $id) {
$definition->addMethodCall('addBannerType', [new Reference($id)]);
}
}
}
如您所见,这个 class 应该实现 CompilerPassInterface
。您可以观察到我正在寻找标记为 banner.process_banners 的特定服务。稍后我将展示如何标记服务。然后,我从 BannerManager
.
调用 addBannerType
方法
App\Service\BannerManager.php:(在你的情况下 src/BUNDLE/Service/BannerManager.php)
class BannerManager
{
/**
* @var array
*/
private $bannerTypes = [];
/**
* @param BannerInterface $banner
*/
public function addBannerType(BannerInterface $banner)
{
$this->bannerTypes[$banner->getType()] = $banner;
}
/**
* @param string $type
*
* @return BannerInterface|null
*/
public function getBannerType(string $type)
{
if (!array_key_exists($type, $this->bannerTypes)) {
return null;
}
return $this->bannerTypes[$type];
}
/**
* Process request and return banner.
*
* @param string $type
* @param Server $server
* @param Request $request
*
* @return Response
*/
public function process(string $type, Server $server, Request $request)
{
return $this->getBannerType($type)->process($request, $server);
}
}
这个 class 有一个名为 process()
的自定义方法(由我创建)。您可以随意命名,但我认为这很冗长。所有参数都是我发过来的,不用介意。想发什么就发什么
现在我们有了我们的管理器并设置了编译器通道。是时候设置我们的横幅类型(基于我的示例)并标记它们了!
我的横幅类型在 src/Service/Banner/Types 下(在你的情况下应该是 src/BUNDLE/Service/WhateverYouWant/Type。这不是重要!您可以稍后更改它 services.yaml).
这些类型正在实现我的 BannerInterface
。在本例中 class 下的代码无关紧要。还有一件事我应该警告你!您应该会在 BannerManager 下看到 addBannerType()
我正在调用 $banner->getType()
。在我的例子中,这是从 BannerInterface 继承的一种方法,它有一个唯一的字符串(在我的例子中,我有三种横幅类型:小、普通、大)。此方法可以有任何名称,但不要忘记在您的管理器中更新它。
我们快准备好了!我们应该标记它们,然后我们准备尝试它们!
转到您的 services.yaml 并添加这些行:
App\Service\Banner\Types\:
resource: '../src/Service/Banner/Types/'
tags: [banner.process_banners]
请看标签!
无论我想显示自定义横幅,我都使用一个简单的 URL 和 $_GET,我保留我的横幅类型,然后像这样加载它:
public function view(?Server $server, Request $request, BannerManager $bannerManager)
{
...
return $bannerManager->getBannerType($request->query->get('slug'))->process($request, $server);
}
我有 2 个服务,BlueWorkerService
和 YellowWorkerService
,都实现相同的接口,WorkerServiceInterface
。这些服务中的每一个都使用相同的实体,但具有不同的所需逻辑。
我需要注入其中一个,而不是两个,类 并在 ProcessorService
中使用它们,以便在正确的 Worker 上调用接口方法。使用哪个 Worker 服务取决于当前正在处理的 Worker。我会分解它:
Class WorkerProcessor {
private $workerService;
public function __construct(WorkerServiceInterface $workerServiceInterface)
{
$this->workerService = $workerServiceInterface;
}
public function getMixedColourWithRed() {
return $this->workerService->mixWithRed();
}
}
正在使用的工作人员服务将基于正在处理的工作人员是否具有 Blue
或 Yellow
的 colour
属性。
我知道我可能可以使用 Factory 来实现这个 as described here 但我的问题是如何告诉工厂我正在处理哪种 Worker 颜色?
运行 在 Symfony 上 3.4
如果您需要更多信息,请提问,我会更新问题。
注意:我使用的是 Symfony 4.3.1。我会 post 那样做,然后我会帮助您将所有代码从该架构移至 Symfony 3.4。
我正在使用类似的概念在我的项目中加载不同的 classes。我先解释一下,然后我会在这段文字下添加代码。
首先,我在 src/Kernel.php
下加载自定义编译器传递(您的文件是 app/AppKernel.php
):
/**
* {@inheritDoc}
*/
public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new BannerManagerPass());
}
BannerManagerPass
它是在 src/DependencyInjection/Compiler
下创建的(在您的情况下应该是 src/BUNDLE/DependencyInjection/Compiler`)。
class BannerManagerPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(BannerManager::class)) {
return;
}
$definition = $container->findDefinition(BannerManager::class);
$taggedServices = $container->findTaggedServiceIds('banner.process_banners');
foreach (array_keys($taggedServices) as $id) {
$definition->addMethodCall('addBannerType', [new Reference($id)]);
}
}
}
如您所见,这个 class 应该实现 CompilerPassInterface
。您可以观察到我正在寻找标记为 banner.process_banners 的特定服务。稍后我将展示如何标记服务。然后,我从 BannerManager
.
addBannerType
方法
App\Service\BannerManager.php:(在你的情况下 src/BUNDLE/Service/BannerManager.php)
class BannerManager
{
/**
* @var array
*/
private $bannerTypes = [];
/**
* @param BannerInterface $banner
*/
public function addBannerType(BannerInterface $banner)
{
$this->bannerTypes[$banner->getType()] = $banner;
}
/**
* @param string $type
*
* @return BannerInterface|null
*/
public function getBannerType(string $type)
{
if (!array_key_exists($type, $this->bannerTypes)) {
return null;
}
return $this->bannerTypes[$type];
}
/**
* Process request and return banner.
*
* @param string $type
* @param Server $server
* @param Request $request
*
* @return Response
*/
public function process(string $type, Server $server, Request $request)
{
return $this->getBannerType($type)->process($request, $server);
}
}
这个 class 有一个名为 process()
的自定义方法(由我创建)。您可以随意命名,但我认为这很冗长。所有参数都是我发过来的,不用介意。想发什么就发什么
现在我们有了我们的管理器并设置了编译器通道。是时候设置我们的横幅类型(基于我的示例)并标记它们了!
我的横幅类型在 src/Service/Banner/Types 下(在你的情况下应该是 src/BUNDLE/Service/WhateverYouWant/Type。这不是重要!您可以稍后更改它 services.yaml).
这些类型正在实现我的 BannerInterface
。在本例中 class 下的代码无关紧要。还有一件事我应该警告你!您应该会在 BannerManager 下看到 addBannerType()
我正在调用 $banner->getType()
。在我的例子中,这是从 BannerInterface 继承的一种方法,它有一个唯一的字符串(在我的例子中,我有三种横幅类型:小、普通、大)。此方法可以有任何名称,但不要忘记在您的管理器中更新它。
我们快准备好了!我们应该标记它们,然后我们准备尝试它们!
转到您的 services.yaml 并添加这些行:
App\Service\Banner\Types\:
resource: '../src/Service/Banner/Types/'
tags: [banner.process_banners]
请看标签!
无论我想显示自定义横幅,我都使用一个简单的 URL 和 $_GET,我保留我的横幅类型,然后像这样加载它:
public function view(?Server $server, Request $request, BannerManager $bannerManager)
{
...
return $bannerManager->getBannerType($request->query->get('slug'))->process($request, $server);
}