Laravel : Dependency injection vs Facades?
Asked Answered
C

4

13

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('/');
    }
    ...
}
Curcuma answered 22/11, 2014 at 18:10 Comment(4)
This question appears to be off-topic because it should be on programmers.stackexchange.comCuneate
Do you know why you should inject objects instead of using Laravel's facades?Cuneate
@FlorianMargaine , should be "facades".Interjoin
I think doing this depends on your opinion of IOC binding. If the object will be used within all/most of your controller methods, it makes sense to pass to the construct. Example might be: meta(title,description,etc). You would then add your binding elsewhere If on the other hand you are only using a class sparsely throughout the application, a facade makes more sense. Example: Validator, Session, Redirect. (in the context of a controller method)Immune
M
3

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).
Ml answered 27/1, 2016 at 1:44 Comment(0)
W
1

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');
    }

}
Wennerholn answered 4/2, 2017 at 10:34 Comment(0)
S
0

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::...).

Sisal answered 27/2, 2017 at 0:10 Comment(0)
H
-1

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.

Hanhhank answered 13/11, 2017 at 19:56 Comment(1)
Using both of the methods you described creates an unnecessary dependency in the code/tests and relies on Laravels magic to resolve the static Auth:: call. Second uses a global function call again creating a dependency. Better to initialize the dependency in the constructor and use that.Subhuman

© 2022 - 2024 — McMap. All rights reserved.