У меня есть 2 сервиса, BlueWorkerService и YellowWorkerService, оба реализуют один и тот же интерфейс, WorkerServiceInterface. Каждая из этих служб использует одни и те же сущности, но с разной требуемой логикой.
Мне нужно внедрить один из этих классов, но не оба, и использовать их в ProcessorService, чтобы методы интерфейса вызывались с помощью правильного Worker. Какой рабочий сервис использовать, зависит от того, какой рабочий процесс обрабатывается в данный момент. Я сломаю это:
Class WorkerProcessor {
private $workerService;
public function __construct(WorkerServiceInterface $workerServiceInterface)
{
$this->workerService = $workerServiceInterface;
}
public function getMixedColourWithRed() {
return $this->workerService->mixWithRed();
}
}
Используемый рабочий сервис будет зависеть от того, имеет ли обрабатываемый рабочий процесс colour свойство Blue или Yellow.
Я знаю, что, вероятно, могу использовать фабрику для достижения этого как описано здесь, но моя проблема заключается в том, как сообщить фабрике, какой рабочий цвет я обрабатываю?
Запуск на Symfony 3.4
Если вам нужна дополнительная информация, просто спросите, и я обновлю вопрос.
Не можете ли вы внедрить BlueWorkerService вместо WorkerServiceInterface?
@GasKa Не могли бы вы объяснить подробнее? @DylanKas Как говорится в вопросе, это должно быть динамическим. Каждый раз, когда вызывается процессор, он может использовать разные экземпляры WorkerServiceInterface




ПРИМЕЧАНИЕ. Я использую Symfony 4.3.1. Выложу так, потом помогу перенести весь код с этой архитектуры на Symfony 3.4.
Я использую аналогичную концепцию для загрузки разных классов в свой проект. Позвольте мне сначала объяснить, затем я добавлю код под этот текст.
Во-первых, я загружаю собственный проход компилятора под 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)]);
}
}
}
Как видите, этот класс должен реализовать CompilerPassInterface. Вы можете заметить, что я ищу определенные службы, помеченные как баннер.process_banners. Чуть позже я покажу, как я пометил сервисы. Затем я вызываю метод addBannerType из BannerManager.
Приложение\Сервис\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);
}
}
У этого класса есть собственный метод (созданный мной) под названием process(). Вы можете назвать его как хотите, но я думаю, что это довольно многословно. Все параметры присланы мной, так что не обращайте внимания. Вы можете отправить все, что хотите.
Теперь у нас есть наш менеджер и установлен проход компилятора. Пришло время установить типы наших баннеров (на основе моего примера) и пометить их!
У меня типы баннеров находятся под src/Сервис/Баннер/Типы (в вашем случае должно быть src/BUNDLE/Service/WhateverYouWant/Type. Это не имеет значения! Вы можете изменить его позже из services.yaml).
Эти типы реализуют мой BannerInterface. В этом случае не имеет значения код под классом. Еще одна вещь, о которой я должен вас предупредить! Вы должны увидеть это в 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);
}
Спасибо, что помогли мне понять, как работает проход компилятора. Это хорошее решение моей проблемы
Вам нужен шаблон Strategy, а не Factory.