Внедрение произвольного параметра в сервис - PullRequest
0 голосов
/ 28 декабря 2018

Мне интересно, есть ли какая-то лучшая практика для следующего случая.

Например, у меня есть несколько служб, и я внедряю их все в виде массива в службу "фабрики".Тогда я вызываю метод этой фабрики и хочу получить только одну услугу в зависимости от некоторых условий.После этого я запускаю эту службу и получаю результат ...

Однако для некоторых из этих служб требуется случайная строка, которую я получаю из запроса клиента.

Конечно, я могу вызватьМетод сервиса с этой строкой в ​​качестве параметра, но некоторые сервисы не требуют этой строки, и я получу «неиспользуемую переменную» в методе.

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

Есть ли более элегантное решение для передачи параметров, которые я не мог бы ввести в службу или использовать для этого установщик?

Вот как это выглядит вмой код

Во-первых, у меня есть интерфейс всех серверов, которые я хочу проверить.Служба должна поддерживать клиента, а затем предоставлять информацию из DTO.

interface Renderable {
     public function supports(Customer $customer);
     public function render(CustomerDTO $dto);
}

Далее у меня есть несколько служб.Этот использует DTO для визуализации данных.

class ServiceOne implements Renderable
{  
    public function suppots(Customer $customer)
    {
        return $customer->getPriority() === 1;
    }

    public function render(CustomerDTO $dto)
    {
        return 'One: '.$dto->getName();
    }
}

Однако некоторые сервисы не нуждаются в DTO для визуализации, они просто предоставляют жестко закодированное значение.

class ServiceTwo implements Renderable
{
    public function suppots(Customer $customer)
    {
        return $customer->getPriority() !== 1;
    }
    // service does not use DTO, it simply output result
    // so, I'll get a notice about unused variable
    // and I can not remove it from the method since it is in interface
    public function render(CustomerDTO $dto)
    {
        return 'Two';
    }
}

Это фабрика.Все сервисы внедрены в виде массива.Затем он проверяет и возвращает первый сервис, который поддерживает экземпляр клиента.

class ServiceFactory
{
    /** @var Renderable[] */
    private $services;

    public function __construct(iterable $services)
    {
        $this->services = $services;
    }

    public function getRenderer(Customer $customer)
    {
        foreach ($this->services as $service)
        {
            if ($service->supports($customer)
            {
                return $service;
            }
        }
    }
}

Здесь я использую фабрику и ее результат

$customer = ...; // it comes from a database
$request = ...; // it comes from a http request

$renderService = $factory->getRenderer($customer);

$customerDTO = CustomerDTO::createFromData([
    'customerUid' => $customer->getUid(),
    'date' => new \DateTime(),
    'name' => $request->getSheetUid(),
    'tags' => $request->getTags(),
]);

$renderService->render($customerDTO);

Итак, мне нужно вызвать Renderer :: renderс экземпляром DTO.Но некоторые сервисы не используют его для «рендеринга» данных.Я также не могу внедрить его в службу рендеринга, поскольку этот объект (DTO) создается во время выполнения, когда все службы уже внедрены.Я также не могу ввести RequestStack в сервис.

1 Ответ

0 голосов
/ 28 декабря 2018

Поскольку ваш параметр поступил из запроса - он не может быть напрямую введен в сервис.В зависимости от реальной логики ваших услуг вы можете рассмотреть один из подходов, перечисленных ниже.Давайте назовем вашу «случайную строку, полученную из запроса клиента», для * $requestParam для дальнейшего использования.

В обоих случаях вам нужно будет получить ваш $requestParam от фактического Request объекта и передать его куда-нибудь еще,Это можно сделать по-разному, я бы предложил создать слушатель (например, RequestParamListener) для события kernel.request и поместить сюда фрагмент кода, который принимает параметр из Request и передает его дальше.в этот слушатель.В подходах, перечисленных ниже, я предполагаю, что $requestParam будет проходить таким образом.

1.Отдельный поставщик

Вы можете создать отдельный класс (например, RequestParamProvider), который будет выступать в качестве поставщика этого $requestParam для других услуг.Он будет получать $requestParam от RequestParamListener, а другим службам, которые должны получить $requestParam, потребуется внедрить этого провайдера и использовать его метод getRequestParam() для получения требуемого параметра.

С моей точки зрения, онэто самый простой подход, и я бы порекомендовал его.

2.Прямой впрыск по фабрике

Поскольку у вас есть заводские услуги - вы можете передать это $requestParam непосредственно на фабрику и позволить ему инициализировать другие службы.Менее гибкий, потому что вам нужно будет реализовать саму логику инициализации и поддерживать ее в процессе развития проекта.

3.Прямое внедрение с использованием интерфейса

Вы можете создать отдельный интерфейс (например, RequestParamAwareInterface), который будет содержать метод setRequestParam(), и разрешить всем классам, которым требуется этот $requestParam, реализовать этот интерфейс.После этого вам нужно написать отдельный проход компилятора , который соберет все такие классы (путем итерации по ContainerBuilder и поиска реализации конкретного интерфейса по классам внутри Definition службы) и passмассив этих услуг для вашего RequestParamListener.Слушатель, в свою очередь, будет обязан передавать $requestParam для каждой из предоставленных услуг.

Этот подход позволит вашему приложению расти без необходимости синхронизации $requestParam логики внедрения.Однако это будет происходить за счет предварительной реализации всех задействованных служб независимо от фактического дальнейшего использования созданных экземпляров.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...