Развязка вывода в командной строке Symfony

#php #symfony #decoupling

#php #symfony #развязка

Вопрос:

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

Я использую Symfony Console component для создания консольного приложения. Я пытаюсь свести связывание классов к минимуму, поскольку это упрощает модульное тестирование и, как правило, является лучшей практикой.

В моем приложении есть некоторые процессы, выполнение которых может занять некоторое время, поэтому я хочу информировать пользователя о том, что происходит, используя индикаторы выполнения и общий текстовый вывод по мере его выполнения. Symfony требует, чтобы экземпляр Symfony консольного OutputInterface передавался в метод выполнения любой команды. Пока все хорошо; Я могу создавать индикаторы выполнения и выводить текст по своему усмотрению. Однако вся тяжелая работа моего приложения происходит не в методах выполнения команд, а в основных классах моего приложения. Эти классы не должны и не знают, что они используются в консольном приложении.

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

Может кто-нибудь указать мне правильное направление, пожалуйста? Я чувствую, что это должно быть довольно распространенным случаем, но не могу найти много об этом.

Заранее спасибо за ваше время!

Ответ №1:

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

 <?php

$progress = $this->getHelperSet()->get('progress');
$dispatcher = $this->getContainer()->get('event_dispatcher');

$dispatcher->addListener('my_start_event', function (GenericEvent $event) use ($progress, $output) {
    $progress->start($output, $event->getArgument('total'));
});

$dispatcher->addListener('my_progress_event', function () use ($progress) {
    $progress->advance();
});

$dispatcher->addListener('my_finish_event', function () use ($progress) {
    $progress->finish();
});
  

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

Комментарии:

1. Это звучит как самое простое решение. Я думаю, по крайней мере, тогда мое ядро приложения не будет знать о пользовательском интерфейсе и будет дополнительно заниматься запуском событий выполнения. Ах да, я понимаю, что вы имеете в виду, говоря о расширении моего сервиса (например, EventAwareService или что-то в этом роде) и наличии защищенного метода в исходном сервисе.