0

I have been asking around, if it's wrong to create instances of the model layer in the controller, even though if it's hidden behind a factory like this:

parent::getServiceFactory()->create('Car');

I have got told to use A dependency injection container to initialize my controller(s). I have been googling all night, last night, I understood that Dependency injection Container (DiC) is used to create an instance of an object, and inject the needed objects into it, without doing it manually in the router - Maybe I just misunderstood the concept.

I have read this post How to build a PHP Dependency Injection Container, It shows the following line of code:

$ioc->register('database', new DatabaseServiceProvider($host, $user, $pass))

Now, I assume $host, $user, $pass are just string variables, now if I want to pass objects to a controller, how can I dynamically know which objects it needs to have injected?

Is it right to add an array of types like this?:

$container->register('SomeController', array('Namespace\to\Model1', 'Namespace\to\Model2'));

and then that container register will create an object of SomeController, which will have Model1, Model2 objects

Is that wrong to do? What is the correct way of using DI in this case? Also are Domain objects & data mappers part of the model layer?

Or is it right to initialize all controllers object in the container when the application loads, then I can easily use any controller?

Community
  • 1
  • 1
Artemkller545
  • 979
  • 3
  • 21
  • 55
  • You should probably first understand what a DI container is and why you would want to use it, before you attempt to actually use it. – Robert Harvey May 10 '14 at 16:19

1 Answers1

0

I have got told to use A dependency injection container to initialize my controller(s). I have been googling all night, last night, I understood that Dependency injection Container (DiC) is used to create an instance of an object, and inject the needed objects into it.

That is correct.

I have read this post How to build a PHP Dependency Injection Container

Stop right there. That answer does not show how to build a container -- it shows how to build a service locator. There's a huge difference between the two:

  • A service locator is like a key/value store: you put named things inside, and later on you ask for them by name and the locator returns them to you.
  • An injection container is like a factory: you ask it to produce something and it produces it for you. The specifics of how the product is created can range from totally opaque to fully specifiable on your part.

In layman's terms, a service locator can do this:

class Perishable
{
    public function __construct(DateTime $expirationDate) { ... }
}

$locator->set('milk', new Perishable(new DateTime());
$milk = $locator->get('milk');

but, in contrast to an injection container, it cannot do this:

$milk = $container->build(Perishable::class); // ::class is a PHP 5.5 feature

An injection container is able to determine on its own that Perishable has a dependency on a DateTime and resolve that dependency. This happens recursively, with the understanding that all the "leaf" dependencies can be instantiated. In this case DateTime is easily instantiated because it's a concrete class with a constructor that can run parameterless; in other scenarios you would have to help by telling the container what to do if it comes upon a dependency that cannot be directly instantiated, e.g. an Iterator.

Now, I assume $host, $user, $pass are just string variables, now if I want to pass objects to a controller, how can I dynamically know which objects it needs to have injected?

We have now reached the implementation part: how do you know that a Perishable needs a DateTime, and how do you create that DateTime?

You can do this by using reflection:

$class = new ReflectionClass('Perishable');
$constructor = $class->getConstructor();
$arguments = [];
foreach ($constructor->getParameters() as $parameter) {
    $argumentClass = $parameter->getClass();
    $arguments[] = $argumentClass->newInstance();
}

$result = $class->newInstanceArgs($arguments);

This is barebones code that can instantiate a Perishable as given above, but of course it will fail badly in more general cases (there's a lot more you have to properly take care of, e.g. recursively resolving constructor arguments).

Is it right to add an array of types like this?

No, because you would have to violate DRY: the dependencies for SomeController would be specified both as part of its constructor definition and when registering it into the container or locator.

Or is it right to initialize all controllers object in the container when the application loads, then I can easily use any controller?

It's not right at all, and as explained above here you are using the word "container" when in fact you have a mental picture of a service locator. In general, service locator has many drawbacks that an injection container does not. In this situation you should use a proper container instead.

Jon
  • 428,835
  • 81
  • 738
  • 806