Динамическое использование сервиса - PullRequest
0 голосов
/ 07 января 2019

Я хотел бы обработать данные в таблице, используя службу, с использованием правильной службы, указанной в записи. Итак, предположим, у меня есть таблица со следующими данными

| id | value1  | value2  | service        | result |
|----|---------|---------|----------------|--------|
| 1  | string1 | string2 | string_version |        |
| 2  | int1    | int2    | int_version    |        |
| 3  | string3 | string3 | string_version |        |

Я хочу обработать каждую строку в родительском сервисе calculator, он зацикливает каждую запись и получает сервис на основе значения service, сервис вычисляет результат, используя value1 и value2, а затем сохраняет результат в result

На данный момент я создал сервис calculator, один из параметров которого является контейнером сервиса - тогда я могу использовать get, чтобы получить нужный мне сервис:

class Calculator {
  private $container;

  public function __construct(ContainerInterface $container) {
     $this->container = $container;
  }

  public function calcResult($record) {
    $service = $this->container->get($record->getService());
    $result = $service->process($record->getValue1(),$result->getValue2());
    $record->setResult($result);
  }

}

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

Это очень упрощенная версия того, что я пытаюсь сделать, но возможность динамически передавать / получать услугу от другого сервиса - это то, что я пытаюсь сделать.

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

1 Ответ

0 голосов
/ 08 января 2019

У вас есть два очевидных кандидата:

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

Примечание : у вас будут дубликаты и немного некрасивого кода в вашем сервисе, если вы используете сервисный локатор, как показано ниже.

ЛОКАТОР ОБСЛУЖИВАНИЯ

interface ServiceLocatorInterface
{
    public function locate(string $id);
}

-

use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;

class ServiceLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
    private $locator;

    public function __construct(ContainerInterface $locator)
    {
        $this->locator = $locator;
    }

    public static function getSubscribedServices()
    {
        return [
            'string_version' => StringCalculator::class,
            'int_version' => IntCalculator::class,
        ];
    }

    public function locate(string $id)
    {
        if (!$this->locator->has($id)) {
            throw new ServiceLocatorException('Service was not found.');
        }

        try {
            return $this->locator->get($id);
        } catch (ContainerExceptionInterface $e) {
            throw new ServiceLocatorException('Failed to fetch service.');
        }
    }
}

-

class StringCalculator
{
    public function calculate($value1, $value2)
    {
        return $value1.' - '.$value2;
    }
}

-

class IntCalculator
{
    public function calculate($value1, $value2)
    {
        return $value1 + $value2;
    }
}

Использование:

class YourService
{
    private $serviceLocator;

    public function __construct(\App\ServiceLocatorInterface $serviceLocator)
    { 
        $this->serviceLocator = $serviceLocator;
    }

    public function yourMethod()
    {
        /** @var StringCalculator $calculator */
        $calculator = $this->serviceLocator->locate('string_version');
        $result = $calculator->calculate('1', '2'); // result: 1 - 2

        /** @var IntCalculator $calculator */
        $calculator = $this->serviceLocator->locate('int_version');
        $result = $calculator->calculate(1, 2); // result: 3
    }
}

СЕРВИСНЫЕ УСЛУГИ

service:
    App\Strategy\Calculator:
        arguments: [!tagged calculator]

    App\Strategy\StringCalculatorStrategy:
        tags:
            - { name: calculator }

    App\Strategy\IntCalculatorStrategy:
        tags:
            - { name: calculator }

-

use Traversable;

class Calculator
{
    private $calculators;

    public function __construct(Traversable $calculators)
    {
        $this->calculators = $calculators;
    }

    public function calculate(string $serviceName, $value1, $value2)
    {
        /** @var CalculatorStrategyInterface $calculator */
        foreach ($this->calculators as $calculator) {
            if ($calculator->canProcess($serviceName)) {
                return $calculator->process($value1, $value2);
            }
        }
    }
}

-

interface CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool;

    public function process($value1, $value2);
}

-

class StringCalculatorStrategy implements CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool
    {
        return $serviceName === 'string_version';
    }

    public function process($value1, $value2)
    {
        return $value1.' '.$value2;
    }
}

-

class IntCalculatorStrategy implements CalculatorStrategyInterface
{
    public function canProcess(string $serviceName): bool
    {
        return $serviceName === 'int_version';
    }

    public function process($value1, $value2)
    {
        return $value1 + $value2;
    }
}

Использование:

class YourService
{
    private $calculator;

    public function __construct(\App\Strategy\Calculator $calculator)
    { 
        $this->calculator = $calculator;
    }

    public function yourMethod()
    {
        // result: 1 - 2
        $result = $this->calculator->calculate('string_version', 1, 2);
        // result: 3
        $result = $this->calculator->calculate('int_version', 1, 2);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...