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?
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...
}
}
Related
I have this this class that is a ServiceProvider
namespace Package\Avatar;
use Illuminate\Support\ServiceProvider;
class AvatarServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
include __DIR__.'/routes.php';
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
try{
$this->app->make('Package\Avatar\AvatarController');
} catch (\Exception $e){
dd($e);
}
}
}
But when I try to access to some url of AvatarCotroller class the screen is Blank, and no show neither error. But whenever I comment this line
$this->app->make('Package\Avatar\AvatarController');
I can get the normal errors of Laravel.
You can get rid of including the routes.php in the boot method of the service provider. Simply use $this->app->call('Package\Avatar\AvatarController#method') to call the method on the controller
try
php artisan optimize : to reuse all frequently used classes php will make an cached class in cache/service.php. So we if add new service we need to run it. We need to use it whenever we add new dependency without using composer.
php artisan cache:clear : clear all the above cache and remap everything
I want to swap out my client call or better i try to make a wrapper around this package, so i dont have to write this everytime, so i made a new ServiceProvider which should call
// Create a new client,
// so i dont have to type this in every Method
$client = new ShopwareClient('url', 'user', 'api_key');
on every request i make.
// Later after the Client is called i can make a Request
return $client->getArticleQuery()->findAll();
SwapiServiceProvider
<?php
namespace Chris\Swapi;
use Illuminate\Support\ServiceProvider;
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* #return void
*/
public function boot()
{
}
/**
* Register any package services.
*
* #return void
*/
public function register()
{
$this->app->singleton(ShopwareClient::class, function () {
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
});
}
}
My Class
...
use LeadCommerce\Shopware\SDK\ShopwareClient as Shopware;
class Swapi
{
public function fetchAllArticles(Shopware $shopware)
{
return $shopware->getArticleQuery()->findAll();
}
}
Testing
I just call it in my routes.php for testing
use Chris\Swapi\Swapi;
Route::get('swapi', function () {
// Since this is a package i also made the Facade
return Swapi::fetchAllArticles();
});
But i get everytime the error
FatalThrowableError in Swapi.php line 18: Type error: Argument 1
passed to Chris\Swapi\Swapi::fetchAllArticles() must be an instance of
LeadCommerce\Shopware\SDK\ShopwareClient, none given, called in
/Users/chris/Desktop/code/swapi/app/Http/routes.php on line 7
So i am asking why this
return new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
);
is not called everytime i call a method e.g $shopware->getArticleQuery()->findAll();
Does anyone know why?
I think there might be some confusion here about Laravel's IoC. When you use return Swapi::fetchAllArticles();, Laravel doesn't know what you are doing because you haven't used the container to build out the Swapi class (even though you have registered one with the container) nor do you have a facade built to access it in that manner. Otherwise PHP is going to complain because your function isn't static.
I just wrote this code and verified that it works as far as Laravel putting it all together.
In my service provider, my register function was this...
public function register()
{
$this->app->singleton('swapi', function($app) {
return new SwapiRepository(
new ShopwareClient(
env('SHOPWARE_URL'),
env('SHOPWARE_USER'),
env('SHOPWARE_KEY')
)
);
});
}
Keep in mind, swapi is really just a key the container will use to find the actual class. There's no need to pass in the entire qualified class name when you can keep it simple and easy.
My SwapiRepository which is really the wrapper for the Shopware SDK.
use LeadCommerce\Shopware\SDK\ShopwareClient;
class SwapiRepository
{
protected $client;
public function __construct(ShopwareClient $client)
{
$this->client = $client;
}
public function fetchAllArticles()
{
return $this->client->getArticleQuery()->findAll();
}
}
At this point, you are basically done. Just add App\Providers\SwapiServiceProvider::class, in the providers array (which you probably have done already) in app/config.php and use your wrapper like so...
$swapi = app('swapi');
$swapi->fetchAllArticles();
Or you can have Laravel inject it into other classes as long as Laravel is building said class.
If you want to build out a facade for this to save yourself a line of code each time you want to use this or for snytactical sugar...
use Illuminate\Support\Facades\Facade;
class Swapi extends Facade
{
protected static function getFacadeAccessor() { return 'swapi'; }
}
Make sure to update your aliases array in app/config.php so that it contains 'Swapi' => App\Repositories\Swapi::class,
And finally you should be able to use it like so...
Swapi::fetchAllArticles();
Please note your namespaces are different than mine so you may need to replace mine with yours. You should also now be able to easily inject Swapi into other classes and even method injected into your controllers where needed.
Just remember if you do that though, make sure you are grabbing instances of those classes from Laravel's service container using the app() function. If you try to build them out yourself using new SomeClass, then you have the responsibility of injecting any dependencies yourself.
New command bus feature inclusion in laravel 5 is getting me confused.
Why and when should I use commands while we can achieve same task in controller itself ?
Command
class PurchasePodcast extends Command implements SelfHandling {
protected $user, $podcast;
/**
* Create a new command instance.
*
* #return void
*/
public function __construct(User $user, Podcast $podcast)
{
$this->user = $user;
$this->podcast = $podcast;
}
/**
* Execute the command.
*
* #return void
*/
public function handle()
{
// Handle the logic to purchase the podcast...
event(new PodcastWasPurchased($this->user, $this->podcast));
}
}
Controller
use Illuminate\Foundation\Bus\DispatchesCommands;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use PurchasePodcast;
abstract class Controller extends BaseController {
use DispatchesCommands, ValidatesRequests;
public function purchasePodcast($podcastId)
{
$this->dispatch(
new PurchasePodcast(Auth::user(), Podcast::findOrFail($podcastId))
);
}
}
Why should I make it complex, while I can straight away do it in controller rather than using command.
The idea comes from "Command Pattern" in which an object is used to encapsulate the information to perform an action.
Using Command pattern could make it easier to organize actions to perform in a software. The command, as an object, is reusable in more than one controller, thus let you DRY (Don't Repeat Yourself).
Command object is also easier to test, because it is decoupled from controller.
Of course there is tradeoff in programming. You will have more classes (and files) when you use command pattern. So, use it when you need it. When you find a complex action to perform, and your controller starts getting fat, maybe you would want to take a look at this pattern.
You don't have to use commands. It all depends on the size of your project. If you can get away with putting stuff in your controller, do it. There is no law here, only good/bad practices. And what is considered good practice isn't always the best option for what you are building.
As you said, why make it complex? Don't.
I have a class that throws errors that extends from the base Exception.
The App's core is Silex Micro-Framework - but due to the fact that I am more fluent in Laravel than Symfony (despite Laravel being derived from it) I tend to use some packages from illuminate (hence the reason why you will see some use Illuminate\)
I want to throw an exception when an error occurs but I am getting the integrated Whoops instead. I would like to return JSON instead. (its not the full stack whoops but Symfony\component\debug instead)
Ideally array('status' => <code>, 'message_content');
Question is: a) how do i do it considering its silex with debug that looks like whoops (this service is on by default). b) is there a way to use my class that extends Exception bellow or am i in need of creating an entire new Exception class that suits my needs instead from scratch
namespace Api\Manager\Validation;
use Illuminate\Support\MessageBag;
class ValidationException extends \Exception {
/**
* #var MessageBag
*/
protected $errors;
/**
* #param string $message
* #param MessageBag $errors
*/
function __construct($message, MessageBag $errors)
{
$this->errors = $errors;
parent::__construct($message);
}
/**
* Get form validation errors
*
* #return MessageBag
*/
public function getErrors()
{
return $this->errors;
}
}
I'm assuming you're using the WhoopsServiceProvider to integrate into Silex - you should have the following in your app.php or index_dev.php.
$app->register(new WhoopsServiceProvider());
You can see the code for this here.
It appears to hardcode the PrettyPageHandler.
$app['whoops.error_page_handler'] = $app->share(function() {
return new PrettyPageHandler;
});
If you fork this provider/copy it into your own, then you should be able to replace this with the JsonRespondeHandler.
You will also need to remove some of the other code in the Provider. e.g. the whoops.silex_info_handler will not work for you as it includes some calls like addDataTable() which I think are specific to the PrettyPageHandler class.
$app['whoops'] = $app->share(function() use($app) {
$run = new Run;
$run->allowQuit(false);
$run->pushHandler($app['whoops.error_page_handler']);
$run->pushHandler($app['whoops.silex_info_handler']);
return $run;
});
Without actually testing the above I believe that should mean all your exceptions caught by Whoops will be rendered in JSON, so you won't (and shouldn't) need to create individual exception classes that render JSON specifically.
Update
It's actually a lot more straightforward than that. Just put the below in your index_dev.php after "$app = new Silex\Application();".
$app->error(function (\Exception $e, $code) use($app) {
return $app->json(array("error" => $e->getMessage()),$code);
});
Then exceptions will be output as JSON. The solution is actually from another SO question here.
Or any framework for that matter.
Using Zend Framework 2 as an example, I have the following table class:
<?php
namespace Contact\Model;
use Zend\Db\TableGateway\TableGateway;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Log\Logger;
class UserContactsTable extends AbstractTableGateway
{
protected $tableGateway;
/**
*
* #var \Zend\Log\Logger Instance
*/
protected $logger;
public function __construct(TableGateway $tableGateway, Logger $logger )
{
$this->tableGateway = $tableGateway;
$this->logger = $logger;
}
/**
* Save a contact
*
* #param \Sms\Model\UserContact $userContact
*/
public function saveUserContact(UserContact $userContact)
{
$data = array(
'user_id' => $userContact->user_id,
'contact_id' => $userContact->contact_id
);
try {
$this->tableGateway->insert($data);
} catch (\Exception $e) {
//log
$this->logger->crit($omeErrMsg);
}
}
}
?>
Should I be logging here? Should I tie my logger in to the table class?
Should I let the saveUserContact function throw an exception if insert fails and catch in the controller and log there?
What are the best practises?
My original idea was to create a class with some constant error messages, such as insert and update failures to be used in the table class by the logger, but I'm not sure what is the correct process here.
This is not really limited to PHP or Zend Framework 2 but just so happens to be the language I am using.
I'd be of the opinion that individual components of a system should be as decoupled as possible. So in this example, if saveUserContact happens to fail then it should probably result in an exception being thrown because this isn't the expected behaviour. This class doesn't need to know about what will happen 'further up the chain', such as error logging.
As you mentioned, it would be better to throw the exception and catch it in your controller (or some other form of listener perhaps), which would then handle the logging.
The benefit of such an approach is that your system will be much easier to test because you'll have less objects to stub when constructing your UserContactsTable (mock) object to test.
Generally, i feel like you should log failures where they happen (unless they're expected, in which case that's noisy) but propagate an exception up the stack (or a wrapper exception) so the caller can decide whether to ignore/retry/fail (and log its own, more business-logic-relevant message).