Dependency Injection

Purpose of the Dependecy Injection (DI) is to free classes from the responsibility for obtaining objects that they need for its
operation (these objects are called services). To pass them these services on their instantiation instead. We'll
talk over:

What is the principle of the Dependency Injection.

How to create DI containers.

What is Dependency Injection?

Dependency Injection (DI) is nothing mysterious or baffling. It can be comprehended into one selfish sentence: “Don't seek
for anything, let someone else do it.” Now let's translate this into programmers' speech. We have a class
Article that represents a blog post:

$article = new Article;
$article->title = '10 Things You Need to Know About Losing Weight';
$article->content = 'Every year millions of people in ...';
$article->save();

Method save() saves the article into the database table articles. It's easy to implement using Nette Database, except one thing: Where is Article supposed to get the database
connection, ie. an object of the class Connection?

Well, we can place it into some global variable like $GLOBALS['database'] or into some static member of a class.
But haven't you heard that use of global variables is bad? It's true, global variables are evil and static members are exactly
the same.

So where else will we get the database connection? DI has the answer: “Don't seek for anything, let someone else do it.” In
other words, if I need a database, someone give it to me, it's not my job. Hah, it's devious, dear DI! Let's do it:

Are you asking, where this code takes the $database? DI gives a straight answer: “Let someone else do it.”
Database connection will be supplied by he who called the code. And so on, and so on. Sure you say that it's not possible to
eternalize the delegation of responsibility. That there must be a zero point. And you're right. There's a creator at the
beginning, he doesn't delegate anything and he creates objects. We call him DI container and you can read about it in a moment.

Why are Global Variables Evil?

Good question. Class Article needs the database connection anyway. But from the first example there's not at all
evident, from where and how it gets it. User of such code might be surprised, that the article really saves and he asks: “Where
did it save?” With the second example using DI, the code is self-explaining.

Imagine, that you're exploring some payment gateway and you write an example:

You run the code, with your card number, and later you'll find, that it really withdrew the money from your account! Shocked
you stare at the list and lament: “Where is my money, how could that happen, I didn't pair it with any payment gateway!”
Class CreditCard did it by herself, found one in some global variable, as mysterious, as Article got the
database connection. Such a thing you're not deducating from the code and you don't even know how to change the gateway to
another, like testing.

Factories

You can protest, that use of DI means more writing, that for creating of an instance of Article you have to handle
the database connection and so. That's true, but don't forget the last time, when “less writing” costed you $1000! No, we
don't want to ease that. Objection is correct an we're gonna add one even bigger: When we find a need for Article to
cache some data, in harmony with DI it will require one more argument with the cache repository. That would mean to adapt the
application at many places: At least everywhere, where Article is instantiated.

What now? The thing has a solution: Instead of manual creating of Article object we make a factory
ArticleFactory, ie. object that creates these Article objects. When Article changes the
constructor, only the factory has to update, nothing more. And where to get the factory in our code? You know… let someone else
do it. :-)

DI Container and Services

By the term “DI container” we mean the initial factory that creates all the objects required to run the application. These
objects are called services. What are the services? Ordinary objects, such as the instance of
Nette\Database\Connection. Only in relation to DI containers we call them services.

Example could be a container, that creates an ArticleFactory object, but also the required database
connection:

Advantage is obvious, we don't need to care, how exactly is the object instantiated, that's a job for the factory. Anyway, the
solution has still two drawbacks. First, there's entry data wired into the code, so we're gonna detach them into a variable:

A more serious drawback is that when we call createArticleFactory() several times, a new database connection is
created each time. This needs to be avoided. We'll add method getService that will keep once created services for
next usage:

And we have a fully functional DI container. As you can see, it's no complicated to write it. . It's notable that the service
itself does not know that it is creating by a container, so it is possible to create any object in PHP without affecting its own
source code.

Nette\DI\Container

Class Nette\DI\Container is a flexible implementation of the
universal DI container. We can create custom containers either statically, ie. inheriting this class, or let them automatically generate, which is a very elegant and powerful solution.

Names of factory methods follow an uniform convention, they consist of the prefix createService + name of the
service starting with first letter upper-cased. Note that the container has already defined the array $parameters for
user parameters and the getService() method.

The container will be generated only once and the code is stored in cache (in directory __DIR__ . '/temp').
Therefore the loading of configuration file is placed in the closure in $loader->load(), so it is called
only once.

During development it is useful to activate auto-refresh mode which automatically regenerate the container when any class or
configuration file is changed. Just in the constructor ContainerLoader append true as the second
argument.

As you can see, using Nette DI is not limited to applications written in Nette, you can use it anywhere.