Why and when should I use command bus vs controller laravel? - php

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.

Related

inject model in laravel controllers constructor

I want to know if this is a good practice to use my model class in controllers in this way :
public function __construct(Rule $rules)
{
$this->rules = $rules;
}
I do not want to repeat myself in my controllers so I want to know what is the best approach for that
You use Dependency Injection - it is very good practice.
According to documentation:
Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* #var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* #param UserRepository $users
* #return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* #param int $id
* #return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}
In this example, the UserController needs to retrieve users from a data source. So, we will inject a service that is able to retrieve users. In this context, our UserRepository most likely uses Eloquent to retrieve user information from the database. However, since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the UserRepository when testing our application.
Read also about Service Container - it is powerful tool:
https://laravel.com/docs/5.6/container
It is a good practice for injecting models in controllers, however, the recommended approach is:
Have a use statement at the top of your controller file
Implement it in the functions that requires access to the model, i would not recommend you do it in your controller
If you have a look at the documentation, you will be able to bind the model directly to your route and eliminate some hassle of Model::find(id) https://laravel.com/docs/5.6/routing#route-model-binding
The constructor approach you presented is recommended in using other classes like repositories, singletons, or whatever functionality you wish to inject, see the docs for more info: https://laravel.com/docs/5.6/container
Hope this helps

Laravel DependencyInjection is this "okay" to do?

To handle some logic of my application I created a Service in App/Services/CarsService.php.
I injected this service in my controller through DependencyInjection like so:
CarsController.php
<?php
namespace App\Http\Controllers;
use App\Services\CarsService;
class CarsController extends Controller
{
/** #var CarsService $carsService */
private $carsService;
/**
* Create a new controller instance.
*
* #param CarsService $carsService
*/
public function __construct(CarsService $carsService)
{
$this->carsService = $carsService;
}
So for example when I want to query all the cars with some parameters provided by the user I do something like this in one of my controller methods:
$cars = $this->carsService->getCars($brand, $type);
This keeps my controller clean and my logic is seperated in a Service, seems pretty good and clean to me.
But my question is actually if this is bad practice to do in Laravel? I imagine that there might be a more "Laravel-y" way to handle this.
You don't want to use a service for that. Inject and use model if you're using Eloquent. Or use repository if you're using Query Builder, raw queries or API. For example:
public function __construct(Car $car)
{
$this->car = $car;
}
$this->car->getByBrandAndType($brand, $type);
If you're asking about is using IoC container a good or a bad practice, it's definitely a good tool to use in any app.

How to neatly handle Exceptions in Artisan Commands

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...
}
}

Can I create one Client and reuse it for all my functional tests?

tl;dr: Will this potentially keep my tests from working properly?
I'm trying to write functional tests for my Symfony project, and working from examples in The Symfony Book. So far, each method of the test class starts out with the same line of code:
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class SomeControllerTest extends WebTestCase
{
public function testSomethingOnRouteX()
{
$client = static::createClient();
// set up and assert
}
public function testSomethingElseOnRouteX()
{
$client = static::createClient();
// different set up and assert
}
}
I would like to remove this redundant code, but I am not sure if I should do this.
I added a constructor where I created the client.
public function __construct()
{
parent::__construct();
$this->client = static::createClient();
}
then in the various test methods I can just use $this->client without needing to create it repeatedly. This seems to work so far (I don't have many tests yet.) But I'm new enough to this framework, and to this type of testing in general that I'm not sure if it will cause problems down the road.
The recommended way to do it is to either use the setUp() method, or the #before hook. Both are called before each test method, so you're safe as the state won't be shared between test cases. Cleanup is also done for you automatically after each test case is run (implemented in the WebTestCase class).
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Client;
class SomeControllerTest extends WebTestCase
{
/**
* #var Client
*/
private $client;
protected setUp()
{
$this->client = self::createClient();
}
public function testSomethingOnRouteX()
{
// set up and assert
$this->client->request('GET', '/something');
}
public function testSomethingElseOnRouteX()
{
// different set up and assert
$this->client->request('GET', '/something-else');
}
}
Alternatively to setUp() you can use the #before hook:
namespace Tests\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Client;
class SomeControllerTest extends WebTestCase
{
/**
* #var Client
*/
private $client;
/**
* #before
*/
protected function setUp()
{
$this->client = self::createClient();
}
// ...
}
If you try it, you should probably use the setUp-method instead. Reusing a client might introduce side effects, which is something you try to avoid. If your tests start to randomly fail, you probably want to try to go back to creating a new client per test. I wouldn't recommend it, but more from a gut feeling than actual bad experience. It should work fine most of the time, but when you suddenly reuse a client with for example a differently set header and it doesn't work you will have a headache.
I don't think there will be a huge performance gain as ui-tests are slow anyway, so trying to have as few test cases is probably a better way to go (look for test pyramid, if you don't know what I mean) if that's your aim.
It may work or may not work, depending on what you're testing.
A new client is like a fresh browser installation. No cookies are set, no history, no actual page, and so on.
If you're testing auth for example, it would be really bad if testCanNotAccessInternalResourceIfNotLoggedIn would use the client which is still logged in because testLogInWithCorrectCredentialsWorks ran before it and therefore fails. Sure, you can make sure you logout the user before accessing the resource but just creating a clean new browser instance is the easiest and least error-prone way to do it.

Can we use class as an dependency injection instead of interface

I've used another class as Dependency Injection is it good to work around or I've messed up the OOP way.
Helper.php
class Helper {
public function getModulePermission($role_id, $module, $type) {
// my work code
}
}
DesignationController.php
use App\Helpers\Helper;
class DesignationController extends Controller {
protected $designation;
protected $helper;
/**
* #param DesignationContract $designation
* #param Helper $helper
*/
public function __construct(DesignationContract $designation, Helper $helper) {
$this->designation = $designation;
$this->helper = $helper;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index(Request $request) {
$permission = $this->helper->getModulePermission($request->id, 'Designation', 'view');
if ($permission) {
return true;
} else {
return view('errors.forbidden');
}
}
So I've a class named Helper which can be accessed within each and every controller for checking permissions but I thought that I've messed up the OOP functionality over here. Is it good to work like it as or I need to create an Interface instead of class
Those are two different OOP concepts. the interface forces whoever class implement it to implement functions stated in the interface (called function signature). So for multiple classes implement the same interface, you will end up with multiple classes implement the same set of functions (wither it is the same function body or not).
The second concept is called, dependency injection or inversion of control in some cases. you inject a class either via class constructor or via setters and you call certain function provided by the injected class. Here you will have the same function called by multiple classes which is good for less modification by using common (injected) class, easier unit-testing (you can mock objects easily), more modular code (you can inject different class if you want different functionality).
So the current situation is good enough but it all depends in what you want to do which stated above.
I don't like the name, it speaks nothing to me or gives me an idea that this class can help me get the permissions of the module. Moreover, with a name like this, one can simply put another method, like lets say Helper::login($id) or you name it, which will instantly break the single responsibility principle (laravel controllers do that anyway).
The injection is relatively OK, perhaps a middleware class would be better place to do that.

Categories