如何巧妙地处理 Artisan 命令中的异常

How to neatly handle Exceptions in Artisan Commands

使用 Lumen 创建一个 API - 喜欢 Laravel 但它附带的所有视图对于我正在创建的项目来说都太过分了。

无论如何,我已经制作了一系列命令,用于收集数据并将其存储到数据库中。

<?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 - 但想看看是否已经有框架创建者 推荐 的方法(documentation/searching 没有帮助)。

我知道另一种方法是将所有命令合并到一个文件中并使用选项,但这会使它变得混乱且难以协作。

有什么建议吗?

答案取决于当命令抛出异常时我们希望应用程序做什么。这个问题没有描述处理异常的理想方式,所以让我们看看几个选项。

Laravel 和 Lumen 项目包含一个中心异常 Handler class,我们可以使用它来定义不同异常的行为。 class 处理网络请求和控制台命令中出现的任何异常。

Laravel 使用 app/Exceptions/Handler.php 中的 report() 方法来确定如何记录异常。我们可以在这里添加报错逻辑:

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 对象的引用,我们可以使用该对象将文本写入控制台命令的输出流。

正如我们从上面猜测的那样,中央异常处理程序旨在处理我们的代码不会在较低级别处理的未捕获异常,因此当我们需要在例外。以类似的方式,我们可以覆盖 app/Console/Kernel.php.

中的 reportException()renderException() 方法

如果我们除了通过显示消息来确认命令抛出异常之外还需要做一些特定的事情,我们真的应该在命令本身中编写这个逻辑。为了避免重复代码,我们可以使用一个 abstract class 三个类似的命令提供了具体的实现:

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

    abstract protected function getItems();
}

此抽象命令强制子 classes 实现 getItems() 方法,class 在 fire() 中自动调用该方法。我们可以向此 class 添加任何其他共享逻辑。子命令只需要定义它们的具体实现getItems(),父class会为它们处理异常:

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