You're right, in contrived examples its usually a bit difficult to see exactly what benefit you're getting. Consider a closer-to-real-world example controller:
class TestController
{
public function __construct(CarRepositoryInterface $car)
{
$this->_repository = $car;
}
public function route($id)
{
return View::make('my.view')->with('car', $this->_repository->find($id));
}
}
Very simple, a repository is being injected into the controller's constructor which is then being used in the route to load a specific car by id. The details here of the repository aren't all that important, and presumably there's a service provider that's binding CarRepositoryInterface
to a concrete CarRepository
implementation:
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind('CarRepositoryInterface', function($app) {
return new EloquentCarRepository(new Car());
});
}
}
So there it is, everytime the controller gets constructed, an EloquentCarRepository
gets created and injected into the constructor for the controller to use.
But wait, what happens if you want to switch from using Eloquent to say, Doctrine? Since we're leveraging dependency injection here you don't have to change a single line of code in your controller (or any other controller that may be using your current implementation). All you have to do is define your other implementation of CarRepositoryInterface
, say, DoctrineCarRepository
, and change one line of code in your service provider:
return new DoctrineCarRepository();
Everything else that depends on CarRepositoryInterface
now magically works. And all of this is thanks to the IoC.
You can also add more complex logic to your service provider:
public function register()
{
$this->app->bind('CarRepositoryInterface', function($app) {
if($app->environment() == 'production') {
return new EloquentCarRepository(new Car());
} else {
return new DoctrineCarRepository(new Car());
}
});
}
Here the EloquentCarRepository
will be used only in the production environment, whereas on any other environment the DoctrineCarRepository
will be used. (This example is only to show how you can gain a lot more control over what type of object gets constructed at runtime, not that I'm advocating actually doing this..)
Addendum
As I stated in my comment, this is the type of usage where you're not quite sure what type of object you're going to need until runtime. There is another usage: Depedency Management.
Suppose you have an object that depends on another object:
class MyObject
{
public function __construct(AnotherObject $obj)
{
$this->obj = $obj;
}
}
Suppose also that AnotherObject
depends on yet another object:
class AnotherObject
{
public function __construct(YetAnotherObject $obj)
{
$this->obj = $obj;
}
}
This can quickly spiral out of control, and you can wind up with long chains of dependencies that need to be satisfied before you can actually create the object. With the IoC, you can just pluck an instance out of the container:
$myObject = app()->make('MyObject');
As long as the IoC can construct all of the dependencies, you don't have to do something like this:
$yetAnotherObj = new YetAnotherObject();
$anotherObj = new AnotherObject($yetAnotherObj);
$myObject = new MyObject($anotherObj);