What's the difference between Laravel auto-injecting and manually specifying dependencies in the constructor body?

I am using the repository pattern in my Laravel project. This pattern is not explained in the official documentation except for this snippet :

You can inject the hint repository defined by your application in the controller constructor. The repository will automatically be resolved and injected into the class.

This is my code as per the documentation:

class CategoriesController extends Controller
{
    protected $repo;

    public function __construct(CategoriesRepository $repo)
    {
        $this->repo = $repo;
    }

      

I am typing CategoriesRepository so that it is automatically loaded into the service container.

However, if I directly create a new instance of the CategoriesController class (without using the Service Container), I have to indicate that I need a new instance of CategoryRepository, for example:

$example = new CategoriesController(new CategoriesRepository());

      

Now, suppose I am writing the following code.

class CategoriesController extends Controller
{
    protected $repo;

    public function __construct()
    {
        $this->repo = new CategoriesRepository();
    }

      

This way, I don't have to load the class through the Service Container and call it passing a new CategoriesRepository instance as an argument, because it is automatically created inside the constructor.

So my question is, would this be bad practice? What's the difference between hinting type as a parameter and creating a new instance inside a constructor?

+3


source to share


1 answer


Here's the beauty of dependency injection:

Complex initialization

class MyController {

     public function __construct(A $a) { }
}

class A {
     public function __construct(B $b) { }
}
class B {
     public function __construct(C $c) { }
}

class C {
     public function __construct(D $d) { }
}
class D {
     public function __construct() { }
}

      

Now you can ask laravel to create this class for you, for example:

$controller = make(MyController::class);

      

or you can do:

$controller = new MyController(new A(new B(new C(new D())))));

      



In addition, you can specify more complex rules for creating variables:

app()->bind(D::class, function ($app) {
       $d = new D();
       $d->setValueOfSomething($app->make(AnotherClass::class));
       return $d;
});

      

Testing

This is one of the benefits of injecting dependency on hand-crafting things. Another is unit testing:

    public function testSomeFunctionOfC() {
        $this->app->bind(D::class, function () {
               $dMock = $this->createMock(D::class);
        });
        $c = make(C::class);
    }

      

Now when you create C, the D class will mock the class you can ensure that works according to your specifications.

+2


source







All Articles