Laravel DependencyInjection is this "okay" to do? - php

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.

Related

Binding to Laravel IoC instead of instantiating again and again

In my app I have a service called "LogService" to log events and other items. I basically need to use this on every controller to log events by users. Instead of having to instantiate this service in each controller, I had two thoughts for accomplishing this.
Option 1: Bind the service into the IoC and then resolve it that way
Option 2: Make a master class with the service in it and then extend it for other classes so they come with the service already bound
I have questions for each of these methods:
Option 1: Is this even possible? If so, would it just be with "App::make()" that it would be called? That way doesn't seem to play too well with IDE's
Option 2: I have done this kind of thing in the past but PHPStorm does not seem to recognize the service from the parent object because it is instantiated by "App::make()" and not through the regular dependency injection.
What would be the best course of action?
Thanks!
You can have it both ways, I think the neatest way would be:
1) Have an interface that describes your class, let's call it LogServiceInterface
2) Create a Service Provider that instantiates your class, like so:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class LoggerServiceProvider extends ServiceProvider
{
/**
* Register bindings in the container.
*
* #return void
*/
public function register()
{
$this->app->bind(LogServiceInterface::class, function($app)
{
return new LogService();
});
}
}
3) Register this service provider in config/app.ph file:
'providers' => [
// Other Service Providers
App\Providers\LoggerServiceProvider::class,
],
4) Now, in controller you can request the instance of something that implements LoggerServiceInterface straight in the constructor:
(Some controller):
<?php namespace App\Http\Controllers;
use Illuminate\Routing\Controller;
use App\Repositories\OrderRepository;
class OrdersController extends Controller {
/**
* The logger service.
* #var LoggerServiceInterface $loggerService
*/
protected $loggerService;
/**
* Create a controller instance.
*
* #param OrderRepository $orders
* #return void
*/
public function __construct(LoggerServiceInterface $loggerService)
{
$this->loggerService = $loggerService;
}
/**
* Show all of the orders.
*
* #return Response
*/
public function index()
{
// $this->loggerService will be an instance of your LoggerService class that
// is instantiated in your service provider
}
}
This way, you have got an easy way to quickly change the implementation of your service, moreover, Phpstorm can handle this very easily.
You will still be able to use app()->make() to obtain an instance of your service.
This, however, will not be automatically picked up by Phpstorm. But you can help it to understand that, all you need to do is to use #var annotation, see:
/**
* #var LoggerServiceInterface $logger
*/
$logger = app()->make(LoggerServiceInterface::class);
That way, Phpstorm will know what to expect from that $logger object.

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

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.

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

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.

Implementing a Custom Field on a Doctrine Entity

I have an Attachment Entity in Doctrine which references a file on Amazon S3. I need to be able to provide a sort of 'Calculated Field' on the Entity that works out what I call the downloadpath. The downloadpath would be a calculated URL, for example http://site.s3.amazon.com/%s/attach/%s where I need to replace the two string values with values on the entity itself (account and filename), so;
http://site.s3.amazon.com/1/attach/test1234.txt
Although we use a Service Layer, I'd like the downloadpath to be available on the Entity at all times without it having to pass through the SL.
I've considered the obvious route of adding say a constant to the Entity;
const DOWNLOAD_PATH = 'http://site.s3.amazon.com/%s/attach/%s'; and a custom getDownloadPath() but I'd like to keep specifics like this URL in my app's configuration, not the Entities class (also, see update below)
Does anyone have any ideas on how I could achieve this?
UPDATE To add to this, I am aware now that I would need to generate a temporary URL with the AmazonS3 library to allow temporary authed access to the file - I'd prefer not to make a static call to our Amazon/Attachment Service to do this as It just doesn't feel right.
Turns out the cleanest way to do this is using the postLoad event like so;
<?php
namespace My\Listener;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\LifecycleEventArgs;
use My\Entity\Attachment as AttachmentEntity;
use My\Service\Attachment as AttachmentService;
class AttachmentPath implements EventSubscriber
{
/**
* Attachment Service
* #param \My\Service\Attachment $service
*/
protected $service;
public function __construct(AttachmentService $service)
{
$this->service = $service;
}
public function getSubscribedEvents()
{
return array(Events::postLoad);
}
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof AttachmentEntity) {
$entity->setDownloadPath($this->service->getDownloadPath($entity));
}
}
}

Categories