Reputation: 210
I want to know how to implement a dependency injection container using php(for study). I get the concept of dependency injection and i can implement it but I can only factor out until controllers of php frameworks.
Meaning the whole instantiation, injections, and etc happen in the controller. Like
class SampleController{
public function action1(){
$sample_object = new ObjectToInject();
$dependent_object = new DependentObject($sample_object);
$dependent_object->doSomething();
...
etc
}
}
Now what I'm saying is the controller gets bloated if the logic gets more complicated. I know that my controller gets bloated, doesn't that mean that it is still not so maintainable?
Questions:
Kindly correct me if me questions are vague. Thanks
Upvotes: 2
Views: 574
Reputation: 7270
This is a really loaded question. To quickly answer your points:
(1) No. That is not correct. While you have to do your wiring somewhere, it should not be in your controllers. Your controllers should receive only the fully built objects they need to do their jobs. Don't give them more than they need. Example:
class UserController
{
public function __construct(UserRepository $UserRepository)
{
$this->UserRepository = $UserRepository;
}
public function showUser($id)
{
$User = $this->UserRepository->find($id);
}
}
Note that UserController
only has UserRepository
as a dependency? UserRepository
meanwhile might look like this:
class UserRepository
{
protected $registry = array();
public function __construct(UserFactory $UserFactory, UserMapper $UserMapper)
{
$this->UserFactory = $UserFactory;
$this->UserMapper = $UserMapper;
}
public function find($id)
{
if (isset($this->registry[$id]) {
return $this->registery[$id];
}
$User = $this->UserMapper>find($id);
$this->registry[$User->id] = $User;
return $User;
}
public function findOrNew($id)
{
if (!$User = $this->UserMapper->find($id)) {
$User = $this->UserFactory->make(array('id' => $id));
}
return $User;
}
}
Your UserController
should have no knowledge of the UserFactory
or the UserMapper
objects if it does not need them directly. Further, your UserRepository
should not know about the DataAccessLayer
that gets injected into UserMapper
. UserMapper
also doesn't need to know that DataAccessLayer
is relying on a PDOConnection
object instead of something else.
(2) The use of a container is to do what you SHOULDN'T be doing in your controllers: wiring up all of your dependencies. The simplest, easiest way to understand what a DI Container is and how it works is to download and use Pimple. It's a really simple, straight-forward DI container and it's well documented: https://github.com/fabpot/Pimple
(3) That's a big topic. Symfony's is a more advanced version of Pimple. I suggest learning Pimple and then reading through the documentation for Symfony and seeing the examples they use. Also check out Laravel's, or even PHP-DI https://github.com/mnapoli/PHP-DI
(4) As before see Pimple: https://github.com/fabpot/Pimple
(5) You should strive to test all of your code logic as long as there is something to test. Presumably, each method/function/class in your application does something, so you should test that it's doing what it should do. What you don't have to do, is test that any of its dependencies are doing what they should do, because you are presumably testing those. You trust that the data being passed into one class or method has been tested, and then you test only what the new class/method is meant to be doing.
But again, I highly recommend learning on Pimple, and then trying out something like PHP-DI to see how auto-reflection and auto-resolving dependencies works. The best way to do it is just immerse yourself in a variety of containers and play around with them.
Upvotes: 5
Reputation: 49533
Instantiating dependencies is not "logic", it's just configuration.
AmgLauncher wrote a great answer, I want to provide a simpler one: controllers are no different than services. You should use dependency injection on controllers, just like for services.
Here is the problem: Symfony doesn't encourage that (through their documentation at least). Don't blindly follow Symfony's DIC documentation.
So in the end, the question is: but what/who will construct my controller if it has dependencies?
That's the job of the container. The container will build your controller and all your services. The catch is that some frameworks don't work that way. In Symfony 2, there's an option for that: Controllers as services.
If you were to use Pimple, you would have to write all the code that creates all your controllers. That's useless/boring code. Pimple is not really suited for that.
As AgmLauncher suggested, have also a look at PHP-DI, and for example this guide about how to write controllers. I recommend also you to read this simple introduction to What is a DI container and how it works. (disclaimer: I'm the maintainer of PHP-DI)
Upvotes: 1