Table of Contents
Core Concepts
Throughout this book, we’ll be making use of some of Laravel’s more powerful features.
Before jumping in, it’s important to at least know about Laravel’s container and how it allows us to more easily use Dependency Injection.
This chapter will cover Laravel’s container, its use of Inversion of Control, and Dependency Injection.
The Container
The Illuminate\Foundation\Application
class ties all of Laravel together. This class is a container - it can “contain” data, objects, classes and even closures.
Basic Usage
To see how the container works, let’s run through an exercise in our routes file.
Laravel’s container implements ArrayAccess
, and so we know we can access it like an array. Here we’ll see how we can access it like an associative array.
File: app/routes.php
1
Route
::
get
(
'/container'
,
function
()
2
{
3
// Get Application instance
4
$app
=
App
::
getFacadeRoot
();
5
6
$app
[
'some_array'
]
=
array
(
'foo'
=>
'bar'
);
7
8
var_dump
(
$app
[
'some_array'
]);
9
});
Going to the /container
route, We’ll get this result:
1
array
(
size
=
1
)
2
'
foo
'
=>
string
'
bar
'
(
length
=
3
)
So, we can see that the Application
, while still a class with attributes and methods, is also accessible like an array!
![]() |
FacadesConfused as to what See this and other Facades in the |
Getting More Advanced
Now, let’s get a little fancier with the container and assign a closure:
File: app/routes.php
1
Route
::
get
(
'/container'
,
function
()
2
{
3
// Get Application instance
4
$app
=
App
::
getFacadeRoot
();
5
6
$app
[
'say_hi'
]
=
function
()
7
{
8
return
"Hello, World!"
;
9
};
10
11
return
$app
[
'say_hi'
];
12
});
Once again, run the /container
route and we’ll see:
1
Hello
,
World
!
While seemingly simple, this is actually quite powerful. This is, in fact, the basis for how the separate Illuminate packages interact with each other in order to make up the Laravel framework.
Later we’ll see how Service Providers bind items to the container, acting as the glue between the various Illuminate packages.
Inversion of Control
Laravel’s Container
class has more up its sleeve than simply masquerading as an array. It also can function as an Inversion of Control (IoC) container.
Inversion of Control is a technique which let’s us define how our application should implement a class or interface. For instance, if our application has a dependency FooInterface
, and we want to use an implementing class ConcreteFoo
, the IoC container is where we define that implementation.
Let’s see a basic example of how that works using our /container
route once again.
First, we’ll setup some classes - an interface and an implementing class. For simplicity, these can go right into the app/routes.php
file:
File: app/routes.php
1
interface
GreetableInterface
{
2
3
public
function
greet
();
4
5
}
6
7
class
HelloWorld
implements
GreetableInterface
{
8
9
public
function
greet
()
10
{
11
return
'Hello, World!'
;
12
}
13
}
Now, let’s use these classes with our container to see what we can do. First, I’ll introduce the concept of “binding”.
File: app/routes.php
1
Route
::
get
(
'/container'
,
function
()
2
{
3
// Get Application instance
4
$app
=
App
::
getFacadeRoot
();
5
6
$app
->
bind
(
'GreetableInterface'
,
function
()
7
{
8
return
new
HelloWorld
;
9
});
10
11
$greeter
=
$app
->
make
(
'GreetableInterface'
);
12
13
return
$greeter
->
greet
();
14
});
Instead of using the array-accessible $app['GreetableInterface']
, we used the bind()
method.
This is using Laravel’s IoC container to return the class HelloWorld
anytime it’s asked for GreetableInterface
.
In this way, we can “swap out” implementations! For example, instead of HelloWorld
, I could make a GoodbyeCruelWorld
implementation and decide to have the container return it whenever GreetableInterface
was asked for.
This goes towards maintainability in our applications. Using the container, we can (ideally) swap out implementations in one location without affecting other areas of our application code.
Real-World Usage
Where do you put all these bindings in your applications? If you don’t want to litter your start.php
, filters.php
, routes.php
and other bootstrapping files with bindings, then you can use Service Provider classes.
Service Providers are created specifically to register bindings to Laravel’s container. In fact, nearly all Illuminate packages use a Service Provider to do just that.
Let’s see an example of how Service Providers are used within an Illuminate
package. We’ll examine the Pagination package.
First, here is the Pagination Service Provider’s register()
method:
Illuminate\Pagination\PaginationServiceProvider.php
1
public
function
register
()
2
{
3
$this
->
app
[
'paginator'
]
=
$this
->
app
->
share
(
function
(
$app
)
4
{
5
$paginator
=
new
Environment
(
6
$app
[
'request'
],
7
$app
[
'view'
],
8
$app
[
'translator'
]
9
);
10
11
$paginator
->
setViewName
(
12
$app
[
'config'
][
'view.pagination'
]
13
);
14
15
return
$paginator
;
16
});
17
}
![]() |
The |
So, what’s going on in this register()
method? First and foremost, it registers the “paginator” instance to the container. This will make $app['paginator']
and App::make('paginator')
available for use by other areas of the application.
Next, it’s defining the ‘paginator’ instance as the returned result of a closure, just as we did in the ‘say_hi’ example.
![]() |
Don’t be confused by the use of |
This closure creates a new Pagination\Environment
object, sets a configuration value on it and returns it.
You likely noticed that the Service Provider uses other application bindings! The PaginationEnvironment
class clearly takes some dependencies in its constructor method - a request object $app['request']
, a view object $app['view']
, and a translator $app['translator']
. Luckily, those bindings are created in other packages of Illuminate, defined in various Service Providers.
We can see, then, how the various Illuminate packages interact with each other. Because they are bound to the application container, we can use them in other packages (or our own code!), without actually tying our code to a specific class.
Dependency Injection
Now that we see how the container works, let’s see how we can use it to implement Dependency Injection in Laravel.
What is Dependency Injection?
Dependency Injection is the act of adding (injecting) any dependencies into a class, rather than instantiating them somewhere within the class code itself. Often, dependencies are defined as type-hinted parameters of a constructor method.
Take this constructor method, for example:
1
public
function
__construct
(
HelloWorld
$greeter
)
2
{
3
$this
->
greeter
=
$greeter
;
4
}
By type-hinting HelloWorld
as a parameter, we’re explicitly stating that an instance of HelloWorld
is a class dependency.
This is opposite of direct instantiation:
1
public
function
__construct
()
2
{
3
$this
->
greeter
=
new
HelloWorld
;
4
}
![]() |
If you find yourself asking why Dependency Injection is used, this Stack Overflow answer is a great place to start. I’ll cover some benefits of it in the following examples. |
Next, we’ll see an example of Dependency Injection in action, using Laravel’s IoC container.
Adding Controller Dependencies
This is a very common use case within Laravel.
Normally, if we set a controller to expect a class in its constructor method, we also need to add those dependencies when the class is created. However, what happens when you define a dependency on a Laravel controller? We would need to instantiate the controller somewhere ourselves:
1
$
crtl
=
new
ContainerController
(
new
HelloWorld
);
That’s great, but we don’t directly instantiate a controller within Laravel - the router handles it for us.
We can still, however, inject controller dependencies with the use of Laravel’s IoC container!
Keeping the same GreetableInterface
and HelloWorld
classes from before, let’s now imagine we bind our /container
route to a controller:
File: app/routes.php
1
interface
GreetableInterface
{
2
3
public
function
greet
();
4
5
}
6
7
class
HelloWorld
implements
GreetableInterface
{
8
9
public
function
greet
()
10
{
11
return
'Hello, World!'
;
12
}
13
}
14
15
Route
::
get
(
'/container'
,
'ContainerController@container'
);
Now in our new controller, we can set HelloWorld
as a parameter in the constructor method:
File: app/controllers/ContainerController.php
1
<?
php
2
3
class
ContainerController
extends
BaseController
{
4
5
protected
$greeter
;
6
7
// Class dependency: HelloWorld
8
public
function
__construct
(
HelloWorld
$greeter
)
9
{
10
$this
->
greeter
=
$greeter
;
11
}
12
13
public
function
container
()
14
{
15
return
$this
->
greeter
->
greet
();
16
}
17
18
}
Now head to your /container
route and you should, once again, see:
1
Hello
,
World
!
Note, however, that we did NOT bind anything to the container. It simply “just worked” - an instance of HelloWorld
was passed to the controller!
This is because the IoC container will automatically attempt to resolve any dependency set in the constructor method of a controller. Laravel will inject specified dependencies for us!
Interfaces as Dependencies
We’re not done, however. Here is what we’re building up to!
What if, instead of specifying HelloWorld
as the controller’s dependency, we specified the interface GreetableInterface
?
Let’s see what that would look like:
File: app/controllers/ContainerController.php
1
<?
php
2
3
class
ContainerController
extends
BaseController
{
4
5
protected
$greeter
;
6
7
// Class dependency: GreetableInterface
8
public
function
__construct
(
GreetableInterface
$greeter
)
9
{
10
$this
->
greeter
=
$greeter
;
11
}
12
13
public
function
container
()
14
{
15
echo
$this
->
greeter
->
greet
();
16
}
17
18
}
If we try to run this as-is, we’ll get an error:
1
Illuminate
\
Container
\
BindingResolutionException
:
2
Target
[
GreetableInterface
]
is
not
instantiable
The class GreetableInterface
is of course not instantiable, as it is an interface. We can see, however, that Laravel is attempting to instantiate it in order to resolve the class dependency.
Let’s fix that - when the container sees that our controller depends on an instance of GreetableInterface
, we’ll use the container’s bind()
method to tell Laravel to give the controller and instance of HelloWorld
:
File: app/routes.php
1
interface
GreetableInterface
{
2
3
public
function
greet
();
4
5
}
6
7
class
HelloWorld
implements
GreetableInterface
{
8
9
public
function
greet
()
10
{
11
return
'Hello, World!'
;
12
}
13
}
14
15
// Binding HelloWorld when asked for
16
// GreetableInterface here!!
17
App
::
bind
(
'GreetableInterface'
,
'HelloWorld'
);
18
19
Route
::
get
(
'/container'
,
'ContainerController@container'
);
Now re-run your /container
route, you’ll see Hello, World!
once again!
![]() |
Note that I didn’t use a closure to bind |
Why Dependency Injection?
Why would we want to specify an interface as a dependency instead of a concrete class?
We want to because we need any class dependency given to the constructor to be a subclass of an interface. In this way, we can safely use any implementation - the method we need will always be available.
Put succinctly, we can change the implementation at will, without effecting other portions of our application code.
Here’s an example. It’s something I’ve had to do many times in real applications.
![]() |
Don’t copy and paste this example. I’m omitting some details, such as using configuration variables for API keys, to clarify the point. |
Let’s say our application sends emails using Amazon’s AWS. To accomplish this, we have defined an Emailer
interface and an implementing class AwsEmailer
:
1
interface
Emailer
{
2
3
public
function
send
(
$to
,
$from
,
$subject
,
$message
);
4
}
5
6
class
AwsEmailer
implements
Emailer
{
7
8
protected
$aws
;
9
10
public
function
__construct
(
AwsSDK
$aws
)
11
{
12
$this
->
aws
=
$aws
;
13
}
14
15
public
function
send
(
$to
,
$from
,
$subject
,
$message
)
16
{
17
$this
->
aws
->
addTo
(
$to
)
18
->
setFrom
(
$from
)
19
->
setSubject
(
$subject
)
20
->
setMessage
(
$message
);
21
->
sendEmail
();
22
}
23
}
We bind Emailer
to the AwsEmailer
implementation:
1
App
::
bind
(
'Emailer'
,
function
()
2
{
3
return
new
AwsEmailer
(
new
AwsSDK
);
4
});
A controller uses the Emailer
interface as a dependency:
File: app/controllers/EmailController.php
1
class
EmailController
extends
BaseController
{
2
3
protected
$emailer
;
4
5
// Class dependency: Emailer
6
public
function
__construct
(
Emailer
$emailer
)
7
{
8
$this
->
emailer
=
$emailer
;
9
}
10
11
public
function
email
()
12
{
13
$this
->
emailer
->
send
(
14
'ex-to@example.com'
,
15
'ex-from@example.com'
,
16
'Peanut Butter Jelly Time!'
,
17
"It's that time again! And so on!"
18
);
19
20
return
Redirect
::
to
(
'/'
);
21
}
22
23
}
Let’s further pretend that someday down the line, our application grows in scope and needs some more functionality than AWS provides. After some searching and weighing of options, you decide on SendGrid.
How do you then proceed to change your application over to SendGrid? Because we used interfaces and Laravel’s IoC container, switching to SendGrid is easy!
First, make an implementation of Emailer
which uses SendGrid!
1
class
SendGridEmailer
implements
Emailer
{
2
3
protected
$sendgrid
;
4
5
public
function
__construct
(
SendGridSDK
$sendgrid
)
6
{
7
$this
->
sendgrid
=
$sendgrid
;
8
}
9
10
public
function
send
(
$to
,
$from
,
$subject
,
$message
)
11
{
12
$mail
=
$this
->
sendgrid
->
mail
->
instance
();
13
14
$mail
->
addTo
(
$to
)
15
->
setFrom
(
$from
)
16
->
setSubject
(
$subject
)
17
->
setText
(
strip_tags
(
$message
)
)
18
->
setHtml
(
$message
)
19
->
send
();
20
21
$this
->
sendgrid
->
web
->
send
(
$mail
);
22
}
23
}
Next, (and lastly!), set the application to use SendGrid rather than Aws. Because we have our call to bind()
in the IoC container, changing the implementation of Emailer
from AwsEmailer
to SendGridEmailer
is as simple as this one change:
1
// From
2
App
::
bind
('
Emailer
',
function
()
3
{
4
return
new
AwsEmailer
(
new
AwsSDK
);
5
});
6
7
// To
8
App
::
bind
('
Emailer
',
function
()
9
{
10
return
new
SendGridEmailer
(
new
SendGridSDK
);
11
});
Note that we did this all without changing a line of code elsewhere in our application. Enforcing the use of the interface Emailer
as a dependency guarantees that any class injected will have the send()
method available.
We can see this in our example. The controller still called $this->emailer->send()
without having to be modified when we switched from AwsEmailer
to SendGridEmailer
implementations.
Wrapping Up
Dependency Injection and Inversion of Control are patterns used over and over again in Laravel development.
As you’ll see, we’ll define a lot of interfaces in order to make our code more maintainable, and help in testing. Laravel’s IoC container makes this easy for us.