Как аккуратно обрабатывать исключения в командах Artisan

Используя Lumen для создания API – любите Laravel, но все View, которые приходят с ним, были излишними для проекта, который я создаю.

Во всяком случае, я сделал серию команд, которые выходят и собирают данные и сохраняют их в базе данных.

<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputOption; use App\User; class GetItems extends Command { /** * The console command name. * * @var string */ protected $name = 'GetItems'; /** * The console command description. * * @var string */ protected $description = "Get items and store it into the Database"; /** * Execute the console command. * * @return void */ public function fire() { $this->info("Collecting ..."); $users = User::all(); foreach( $users as $user) { $user->getItems(); } } /** * Get the console command options. * * @return array */ protected function getOptions() { return []; } } 

У меня есть 3 одинаковые команды, каждый из которых собирает несколько разные наборы данных.

Есть ли способ, которым я могу вставить средний слой, который ловит исключение, которое исходит от каждой из функций fire() в моих командах? Я думал о расширении Command Class, но хотел посмотреть, есть ли способ сделать это, что рекомендуется создателями Framework (документация / поиск не помогли).

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

Какие-либо предложения?

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

Проекты Laravel и Lumen включают в себя основной класс Handler исключений, который мы можем использовать для определения поведения для разных исключений. Этот класс обрабатывает любые исключения, которые возникают из веб-запросов и команд консоли.

Laravel использует метод report() в приложении / Исключения / Handler.php, чтобы определить, как регистрировать исключение. Мы можем добавить здесь логику ошибок:

 public function report(Exception $e) { if ($e instanceof CustomConsoleException) { // do something specific... } ... } 

Метод renderForConsole() позволяет нам настроить, как мы хотим отображать сообщения об ошибках и исключениях для консольных команд. Handler исключений проекта обычно не содержит этого определения метода, но мы можем его переопределить в app / Exceptions / Handler.php, если это необходимо:

 public function renderForConsole($output, Exception $e) { $output->writeln('Something broke!'); (new ConsoleApplication)->renderException($e, $output); } 

В приведенном выше примере $output является ссылкой на объект Symfony\Component\Console\Output \OutputInterface который мы можем использовать для записи текста в выходные потоки команды консоли.

Как мы могли бы догадываться выше, центральный обработчик исключений предназначен для обработки исключенных исключений, которые наш код не обрабатывает на более низком уровне, поэтому это не очень полезно, когда нам нужно выполнить некоторые конкретные действия после исключения. Аналогичным образом мы можем переопределить reportException() и renderException() в app/Console/Kernel.php .

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

 abstract class AbstractGetItems extends Command { ... final public function fire() { try { $this->getItems(); } catch (Exception $e) { // handle exception... } } abstract protected function getItems(); } 

Эта абстрактная команда заставляет дочерние классы внедрять метод getItems() , который класс автоматически вызывает в fire() . Мы можем добавить к этому классу любую другую общую логику. Команды-потомки должны только определять свою конкретную реализацию getItems() , и родительский класс будет обрабатывать исключения для них:

 class GetSpecificItems extends AbstractGetItems { ... protected function getItems() { // fetch specific items... } }