Как избежать обслуживания контейнера в заводских моделях - PullRequest
0 голосов
/ 21 ноября 2018

Я использую Symfony 4 для проекта, и у меня есть вопрос, касающийся фабрик.

Предположим, у меня есть стратегия в зависимости от типа строки.

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

Позвольте мне сделать пример:

class ServiceBar implements Doing{
    public function __construct($dep1,$dep2){
    }
    public function do();
}

class ServiceBaz implements Doing{
    public function __construct($dep3,$dep4){
    }
    public function do();
}


// Factory Class
class MyServiceFactory{
    protected $services = [
        'bar' => 'app.service.bar',
        'baz' => 'app.service.baz'
    ];

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

    public function factory($string){
        if(!$this->sc->has($this->services[$string])){
            throw new Exception("Missing Service");
        }
        $this->sc->get($this->services[$string])->do();        

    }

}

// IndexController.php
public function indexAction(Request $request, MyServiceFactory $factory)
{
    $factory->factory($request->get('action'));
}

В этой реализации мои службы созданы со всеми зависимостями, а фабрика вызвана из моего контроллера.

У вас есть другие идеи, комментарии по поводу этого решения?У меня есть сервисный контейнер, введенный в конструктор фабрики;Есть ли другой способ создания услуг с завода?что-то не так с этим подходом?

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Вы можете использовать собственные проходы компилятора, расширения и сервисные локаторы.Это способ, которым Symfony допускает это, но требует большого количества кода.

Массив с автопроводкой

Самый простой подход - это автоматическое связывание аргументов с помощью автопроводного массива .

* 1008.* без зависимости от контейнера без расширения без регистрации пакета 1 проход компилятора

Пример

/**
 * @param Doing[] $doings
 */
public function __construct(array $doings)
{
    $this->doings = $doings;
}

public function create(string $name): Doing
{ 
    foreach ($this->doings as $doing) {
        if ($doing->getName() === name) { // this depends on your design; can be also "is_a" or "instanceof"
            return $doing;
        }
    }

    throw new MissingDoingException;
}

Это также называется шаблон коллектора .

Как интегрировать

0 голосов
/ 21 ноября 2018

A Symfony Сервисный локатор может использоваться, чтобы избежать необходимости вводить весь контейнер.Локатор действует как контейнер, но имеет доступ только к ограниченному количеству сервисов.

Требуется немного волшебства, чтобы настроить все.В вашем случае вам нужен только локатор для доступа к сервисам, реализующим интерфейс Doing.

Начните с локатора, который будет наследовать методы get / has, как и все контейнеры:

use Symfony\Component\DependencyInjection\ServiceLocator;

class DoingLocator extends ServiceLocator
{
    protected $services = [
        'bar' => 'app.service.bar',
        'baz' => 'app.service.baz'
    ];
    public function locate($string) {
        return $this->get($this->services[$string]);
    }
}

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

Начните с того, что ваш класс Kernel будет проходить компилятор:

# src/Kernel.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface

Далее,автоматически помечать все сервисы, которые реализуют интерфейс Doing:

# Kernel.php
protected function build(ContainerBuilder $container)
{
    $container->registerForAutoconfiguration(Doing::class)
        ->addTag('doing');
}

Наконец, добавьте проход компилятора для построения вашей службы локатора:

# Kernel.php
public function process(ContainerBuilder $container)
{
    $doingLocatorIds = [];
    foreach ($container->findTaggedServiceIds('doing') as $id => $tags) {
        $doingLocatorIds[$id] = new Reference($id);
    }
    $doingLocator = $container->getDefinition(DoingLocator::class);
    $doingLocator->setArguments([$doingLocatorIds]);
}

И presto.Вы сделали.Теперь вы можете ввести свой DoingLocator (он же MyServiceFactory), и все должно быть хорошо.

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