Примечание. Я часто обращаюсь к компоненту Symfony Console в своем вопросе, но думаю, что этот вопрос можно считать более широким, если рассматривать его в контексте любого пользовательского интерфейса.
Я использую Symfony Console component
создать консольное приложение. Я пытаюсь свести к минимуму связь между классами, так как это облегчает модульное тестирование и, как правило, является лучшей практикой.
В моем приложении есть несколько процессов, выполнение которых может занять некоторое время, поэтому я хочу держать пользователя в курсе того, что происходит, используя индикаторы выполнения и общий вывод текста во время работы. Symfony требует экземпляра Symfony
Выходной интерфейс консоли, передаваемый методу выполнения любой команды. Все идет нормально; Я могу создавать индикаторы выполнения и выводить текст по своему усмотрению. Тем не менее, вся тяжелая работа моего приложения не происходит в методах выполнения команд, а находится в основных классах моего приложения. Эти классы не должны и не знают, что они используются в консольном приложении.
Я изо всех сил пытаюсь сохранить это таким образом, потому что я не знаю, как обеспечить обратную связь с консолью (или любым другим пользовательским интерфейсом) без внедрения выходного класса в мое ядро. Это приведет к жесткой связи между классом вывода консоли и ядром моего приложения. Я думал об использовании диспетчера событий (у Symfony он есть), но это также означает, что мое ядро будет связано с диспетчером (возможно, это нормально). В идеале мне нужно что-то вроде «пузырить» состояние моего приложения обратно в метод execute вызванной команды, где я могу затем выполнить вывод.
Может ли кто-нибудь указать мне правильное направление, пожалуйста? Я чувствую, что это должно быть довольно распространенным случаем, но я не могу найти много об этом.
Спасибо за ваше время заранее!
Я успешно использовал подход диспетчера событий раньше. Например, вы можете запускать события в начале, в ходе и в конце обработки, а слушатель событий обновляет индикатор выполнения на основании этого.
<?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();
});
Если вы действительно хотите избежать связывания диспетчера событий в вашем сервисе, вы можете расширить или украсить свой класс (возможно, реализуя общий интерфейс) и использовать только диспетчер событий там. Однако вам потребуется точка расширения (открытый или защищенный метод) в базовом классе, чтобы иметь возможность уведомлять о любом прогрессе.
Других решений пока нет …