What, I had been doing previously was to inject only MY MODELS using the constructor and use Facades for the Laravel's provided classes i.e. Session, Auth, Validator etc, for example. Will it be a good idea if I inject each and every class (either mine or Laravel's) through construct and use it by $this->.. syntax or should I inject my own classes using constructor and use Facades for anything provided by Laravel?
To be more specific, here is what my controllers normally look like:
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark ) {
$this->user = $user;
$this->bookmark = $bookmark
}
public function foobar ( ) {
$user_id = Input::get('bar');
...
Session::get('someInfo');
...
return Redirect::to('/');
}
...
}
Should I structure my methods like controller like following, instead?
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark, Input $input, Session $session, Redirect $redirect ) {
$this->user = $user;
$this->bookmark = $bookmark
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Laravel now supports the same Dependency-Injection functionality for route-related methods of classes (not just constructors), such as controllers and middleware.
You could prevent unnecessary injections by only injecting to methods where the dependency is unique, perhaps leaving more common dependencies in the constructor:
class MyController extends BaseController
{
public function __construct( Input $input, Session $session, Redirect $redirect ) {
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( User $user, Bookmark $bookmark ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Conclusion
As for whether you should do it this way, that's up to you, but consider to:
First, use Dependency-Injection vs Facade (which also enables IDE auto-completion).
Then, wherever dependency is unique (and not required by most routes), use per-method injection vs constructor.
Because making all unique dependencies appear in method definition is easier to unit-test (and seems cleaner to me).
It is elegant and useful to inject certain classes, such as Request. In my opinion they should be specified in controller methods where they are needed, as they are then logically connected to the method implementation. Awesome thus far.
I find two facades to be problemmatic - App and Log. Neither are logically connected to a controller or its actions. App and Log are not inputs in any context. As App and Log are utility classes they are relevant to services and repositories as well, and it gets downright nasty if you type hint them in controllers and then pass them on as constructor or method parameters to your support classes.
An additional issue is that App facade does not implement the Illuminate\Contracts\Auth\Guard interface that it proxies, so my IDE lights up with warnings as static analysis is not possible.
For the sake of consistency and overall separation of concerns I would therefore instantiate both App and Log within a constructor or method, depending on how widespread they are used in a class. To make my IDE happy I created the below class to give me a properly typed instance wherever I need it:
<?php namespace App\Components;
use Illuminate\Contracts\Auth\Guard;
use Psr\Log\LoggerInterface;
/**
* Get the underlying object instances of facades from the container.
*/
class AppGlobal
{
/**
* Returns the global logger instance.
*
* #return LoggerInterface
*/
public static function log()
{
return app('log');
}
/**
* Returns the global auth instance, which internally proxies a guard.
*
* #return Guard
*/
public static function auth()
{
return app('auth');
}
}
If you need an object wit properties - put it in as an injection (e.g Input, Session...), otherwise, if you don't store any data in the object and pretty happy using class, than go with facades (e.g Log::..., Redirect::...).
Laravel has replaced many of it's facade with helpers for example
use Auth;
and
Auth::user()
is now just
auth()->user()
this makes thing simpler and neater (also prevents mistakes)
I would suggest using the helpers where possible and if no helper exists, use the facade because it is easier to mock than an injected instance.
Related
In my Symfony project I'll have lot of classes that have similar dependecies (however, the classes are not directly related to each other). For example, most of them requires access to EventBus.
In other framework I was able to specify an interface for the class, for example:
interface EventBusAwareInterface
{
public setEventBus(EventBus $bus);
public getEventBus() : EventBus
}
and then configure DI container to recognize such objects that implements this interface, and call their setEventBus() method with proper argument.
I wonder if there's a method to do the same in Symfony4.
You can use _instanceof directive in your services.yaml like that:
services:
_instanceof:
App\EventBusAwareInterface:
calls:
- method: setEventBus
arguments:
- '#event.bus.service'
My original comment was not quire correct. You can use #inject but it seems to require an additional jms bundle. Could have sworn the container supported it out of the box but I guess not.
However, autowire supports a #required annotation which seems to do the trick.
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
trait RouterTrait
{
/** #var RouterInterface */
protected $router;
/**
* #param RouterInterface $router
* #required
*/
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
// Copied directly from Symfony ControllerTrait
protected function generateUrl(
string $route,
array $parameters = array(),
int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
return $this->router->generate($route, $parameters, $referenceType);
}
protected function redirect($url, $status = 302) : RedirectResponse
{
return new RedirectResponse($url, $status);
}
protected function redirectToRoute($route, array $parameters = array(), $status = 302) : RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
}
Now, any autowired service that uses the RouterTrait will automatically get the router injected.
Yes, something even simpler is very possible. However, I would not encourage over-usage as it can very quickly introduce things like method name collisions and reduce code readability.
That said, Symfony introduced service auto-wiring concept starting with 3.3 (I think), which can be used to have dependency injection with zero-config. In PHP, interfaces cannot contain implementation, but, traits can. So, you could do something like this:
trait FooTraitHandler
{
/**
* #var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
And then:
class RealService
{
use FooTraitHandler;
public function multiply($a, $b)
{
$this->logger->log(LogLevel::ALERT, "Doing some basic math!");
return $a * $b;
}
}
And finally, for example, your controller could inject this RealService service and use multiply method as usual.
So, couple of things worth mentioning:
You do not need pair of getter/setter - trait's member is visible in class utilizing it.
You can utilize many traits, achieving just what you wanted with many interfaces
Finally, if some of utilized traits have methods with same name, you'll get a fatal error. As per official docs:
If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved. To resolve naming conflicts between Traits used in the same class, the insteadof operator needs to be used to choose exactly one of the conflicting methods.
But, in my opinion, doing so deteriorates code readability substantially.
Hope this helps...
I am relatively new to Symfony (version 4) and trying to implement the __construct method for dependency injection.
Currently, I am "injecting" dependencies via my own implementation (before I was aware of the __construct method) like so:
routes.yaml
fetch:
path: /fetch/{req}
controller: App\Controller\Fetch::init
requirements:
req: ".+"
/fetch route calls the init() method, which serves as the constructor.
Controller Class
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use App\Services\Utilities; // a bunch of useful functions
class Fetch extends BaseController {
private $u;
public function init(Utilities $u) {
$this->u = $u; // set the $u member with an instance of $u
}
private function do_fetch(){
$this->u->prettyprint('hello service'); // use one of $u's methods
}
}
If you would indulge me, I came up with this ad-hoc scheme before reading the docs, which detail this almost exactly (I get a cookie).
The one difference is that the docs use __construct() in place of my init() method. The following is an example from the doc page linked above:
// src/Service/MessageGenerator.php
use Psr\Log\LoggerInterface;
class MessageGenerator
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function getHappyMessage()
{
$this->logger->info('About to find a happy message!');
// ...
}
}
But when I swap init() for __construct(), and update the routes.yaml, I get an error.
// .....
class Fetch extends BaseController {
private $u;
public function __construct(Utilities $u) {
$this->u = $u; // set the $u member with an instance of $u
}
// ....
fetch:
path: /fetch/{req}
controller: App\Controller\Fetch::__construct
requirements:
req: ".+"
Its asking me to provide an argument to __construct since that method takes one ($u) but this was not the case when init() was acting as the constructor.
Moreover, I feel like since the __construct() method is a built-in hook, Symfony should know to use it without my having to explicitly tell it to in routes.yaml. However, excluding it throws an error as well.
routes.yaml (__construct not explicitly indicated)
fetch:
path: /fetch/{req}
controller: App\Controller\Fetch
requirements:
req: ".+"
What am I missing here?
__construct is a magic method in PHP. The problem with your init method is that it does not enforce that the object must have an instance of the object you need in order to be built. Sometimes an object property will not be needed. In this case, I recommend creating a setter as a way to optional set that property.Try to make your class properties private, and only allow them to be mutated or retrieved through setters and getters...this will provide a standard API to your obejct, and avoid random state manipulation.
You can use the DIC in Symfony's router to construct your controller instead of extending the base controller class by registering your controllers as services. This greatly decouples you code and allows all kinds of additional flexibility. You should always favor composition over inheritance.
I would like to know If having multiple services injected in controllers constructor is wrong? Is there a way to make it more cleaner?
Example (UserController):
public function __construct(
UserService $userService, NewsService $newsService, PaginateService $paginateService, GroupsService $groupsService, HolidaysService $holidaysService,
CalendarService $calendarService
)
{
parent::__construct();
$this->userService = $userService;
$this->newsService = $newsService;
$this->paginateService = $paginateService;
$this->groupsService = $groupsService;
$this->holidaysService = $holidaysService;
$this->calendarService = $calendarService;
}
In addition to that I also would like to know if it is good to have service in which I inject the repository - for example: I have UserService and in constrcutor I'm injecting the UserRepository.
Example (UserService):
class UserService {
protected $userRepository;
function __construct(UserRespository $userRepository) {
$this->userRepository = $userRepository;
}
public function getUsers() {
$this->userRepository->getAllUsers();
}
}
I personally like to follow a pattern whereby a controller doesn't have any custom methods. Thus you end up with lots of very thin controllers and ultimately will prevent your above situation (one controller dependant on a large number of services).
so I would break up those services into controller use cases
with generic resource methods like index update store create edit destroy.
Another pattern I like to follow is using the app helper instead of injecting dependancies into the constructor.
so you can use app(calendarService::class)->method()
This results in less code and less stuff to forget.
I am using Laravel 5.1 and have set up a Repository pattern. I have the concrete implementations of my repos injected into my controllers. I realize that your SUPPOSED to inject the interface but that over complicates my API and doesn't solve my issue. I have a client config that simply contains a string such as '' and I am already using that globally to use model overrides if they exist.
So, for example, if I have client 'yahoo' and they have an override in my Overrides/Yahoo/Models/User.php then it will use this User.php. Whether its an extension of the base User model or a whole new implementation is up to me.
I am trying to do the same thing for my repositories. I want to be able to put an override file in Overrides/Yahoo/Repos/UserRepository.php and on injection it will either use the base User repository or the override if it exists.
Any ideas of how I can accomplish this? I realize that you can inject a repository interface and use a binding but I want to do this globally. If you can tell me how I can abstract that functionality to automatically bind the correct implementation of the interface based on client config I would accept that.
You can resolve your concrete implementation by the configuration you use, but it requires that you use interfaces.
Example configuration (config/client.php):
<?php
return [
'users' => [
'repository' => \App\Overrides\Yahoo\Repos\UserRepository::class,
],
];
In your AppServiceProviders register method:
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
UserRepositoryInterface::class,
config('client.users.repository')
);
}
In your Yahoo UserRepository, you can then inject the Yahoo User model directly:
<?php
namespace App\Overrides\Yahoo\Repos;
use App\Repos\UserRepositoryInterface;
use App\Overrides\Yahoo\Models\User;
class UserRepository implements UserRepositoryInterface
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
// TODO: Implement UserRepositoryInterface methods
}
Finally, in your UserController you can inject the UserRepositoryInterface and it will bind the concrete implementation based on your configuration:
<?php
namespace App\Http\Controllers;
use App\Repos\UserRepositoryInterface;
class UserController extends Controller
{
private $users;
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
}
It might seem overkill at first, but once you set up everything it's pretty easy to add new overrides.
You could even create a base concrete implementation of the UserRepository and make every override repository inherit from it. This way you don't have to re-implement all methods required by the interface, but you stay flexible when the user repositories use different database technologies (SQL, MondoDB...)
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.