How to neatly handle Exceptions in Artisan Commands

南笙酒味 提交于 2019-12-23 10:47:08

问题


Using Lumen to create an API - love Laravel but all the View's that come with it were overkill for the project I am creating.

Anyway, I've made a series of Commands which go out and collect data and stores it to the database.

<?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 [];
    }

}

I've got 3 similar commands, each one collecting slightly different datasets.

Is there a way I can inject a middle-layer that catches an exception that comes from each of the fire() functions across my Commands? I was thinking of extending the Command Class - but wanted to see if there's already a way to do it that's recommended by the Framework creators (documentation/searching was no help).

I know the alternative would be to combine all the commands into one file and use options, but this makes it messy and harder to collaborate with.

Any suggestions?


回答1:


The answer depends on what we want the application to do when the command throws an exception. The question doesn't describe a desired way to handle the exception, so let's look at a few options.

Laravel and Lumen projects include a central exception Handler class that we can use to define behaviors for different exceptions. This class handles any exceptions that bubble up from web requests and console commands.

Laravel uses the report() method in app/Exceptions/Handler.php to determine how to log an exception. We can add logic here for error reporting:

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

The renderForConsole() method lets us customize how we want to display error and exception messages for console commands. The project's exception Handler usually doesn't contain this method definition, but we can override it in app/Exceptions/Handler.php if needed:

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

    (new ConsoleApplication)->renderException($e, $output);
}

In the example above, $output is a reference to a Symfony\Component\Console\Output \OutputInterface object that we can use to write text to the console command's output streams.

As we might guess from above, the central exception handler is designed to deal with uncaught exceptions that our code doesn't handle at a lower level, so it's not very useful when we need to execute some specific action after an exception. In a similar fashion, we could override the reportException() and renderException() methods in app/Console/Kernel.php.

If we need to do something specific besides just acknowledging that a command threw an exception by showing a message, we really should write this logic in the command itself. To avoid duplicate code, we could use an abstract class that the three similar commands provide concrete implementations for:

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

    abstract protected function getItems();
}

This abstract command forces child classes to implement the getItems() method, which the class calls automatically in fire(). We can add any other shared logic to this class. The child commands need only to define their specific implementation of getItems(), and the parent class will handle exceptions for them:

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


来源:https://stackoverflow.com/questions/46819944/how-to-neatly-handle-exceptions-in-artisan-commands

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!