Внедрить сервис на основе динамического значения в Symfony

У меня есть 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

Если вам нужна дополнительная информация, просто спросите, и я обновлю вопрос.

Вам нужен шаблон Strategy, а не Factory.

GasKa 24.06.2019 08:54

Не можете ли вы внедрить BlueWorkerService вместо WorkerServiceInterface?

Dylan Kas 24.06.2019 08:55

@GasKa Не могли бы вы объяснить подробнее? @DylanKas Как говорится в вопросе, это должно быть динамическим. Каждый раз, когда вызывается процессор, он может использовать разные экземпляры WorkerServiceInterface

Wildcard27 24.06.2019 10:21
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
3
3
1 276
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

ПРИМЕЧАНИЕ. Я использую 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);
}

Спасибо, что помогли мне понять, как работает проход компилятора. Это хорошее решение моей проблемы

Wildcard27 25.06.2019 03:46

Другие вопросы по теме