Внедрить сервис на основе динамического значения в Symfony - PullRequest
2 голосов
/ 24 июня 2019

У меня есть 2 службы, BlueWorkerService и YellowWorkerService, каждая из которых реализует один и тот же интерфейс, WorkerServiceInterface.Каждый из этих сервисов использует одни и те же объекты, но с разной необходимой логикой.

Мне нужно внедрить один из этих классов, но не оба, и использовать их в ProcessorService, чтобы методы интерфейса вызывались с использованием onверный работник.Какой рабочий сервис использовать, зависит от того, какой работник в данный момент обрабатывается.Я разобью это:

Class WorkerProcessor {

  private $workerService;

  public function __construct(WorkerServiceInterface $workerServiceInterface)
  {
    $this->workerService = $workerServiceInterface;
  }

  public function getMixedColourWithRed() {
    return $this->workerService->mixWithRed();
  }
}

Используемая рабочая служба будет зависеть от того, имеет ли обрабатываемый работник свойство colour Blue или Yellow.

Я знаю, что, возможно, смогу использовать Factory для достижения этого , как описано здесь , но моя проблема в том, как сообщить фабрике, какой цвет Worker я обрабатываю?

Работает на Symfony 3.4

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

1 Ответ

1 голос
/ 24 июня 2019

ПРИМЕЧАНИЕ: я использую 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.Вы можете заметить, что я ищу определенные сервисы, помеченные как banner.process_banners .Я покажу, как я пометил службы чуть позже.Затем я вызываю addBannerType метод из BannerManager.

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);
    }
}

В этом классе есть собственный метод (созданный мной) с именем process().Вы можете назвать это как хотите, но я думаю, что это довольно многословно.Все параметры присланы мной, так что не против.Вы можете отправлять все, что захотите.

Теперь у нас есть менеджер и установлен проход компилятора.Пришло время установить типы наших баннеров (на основе моего примера) и пометить их!

Мои типы баннеров находятся в src / Service / Banner / Types (в вашем случае должно быть src / BUNDLE / Service / Wh независимоYouWant / Тип . Это не имеет значения! Вы можете изменить его позже из services.yaml).

Эти типы реализуют мой BannerInterface.В данном случае не имеет значения код под классом.Еще одна вещь, которую я должен предупредить вас!Вы должны увидеть это под BannerManager, внутри addBannerType() я звоню $banner->getType().Это один метод, унаследованный от BannerInterface в моем случае, и он имеет уникальную строку (в моем примере у меня есть три типа баннеров: маленький, нормальный, большой).Этот метод может иметь любое имя, но не забудьте также обновить его в своем менеджере.

Мы почти готовы!Мы должны пометить их, тогда мы готовы их попробовать!

Перейдите на your 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);
}
...