Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is Laravel IoC Container in simple words?

Tags:

laravel

Can anyone explain dependency injection and IoC container in simple and easy words as i am beginner to laravel. thanks

like image 453
user3254411 Avatar asked Jun 30 '14 07:06

user3254411


People also ask

What is Laravel IoC container?

The Laravel inversion of control container is a powerful tool for managing class dependencies. Dependency injection is a method of removing hard-coded class dependencies. Instead, the dependencies are injected at run-time, allowing for greater flexibility as dependency implementations may be swapped easily.

What does IoC container mean?

The terms Inversion of Control (IoC), Dependency Inversion Principle (DIP), Dependency Injection (DI), and IoC containers may be familiar. But are you clear about what each term means?

What is IoC container PHP?

An IoC container is a singleton Class(can only have 1 instance instantiated at any given time) where the specific way of instantiating objects of those class for this project can be registered.

What is Spring IoC container What are the benefits of IoC?

Spring IoC Container is the core of Spring Framework. It creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection(DI) to manage the components that make up the application.


2 Answers

The answer became longer than I wanted it to be in the first place. I included a bit of background information. Still if you're looking for short term explanations, read the first paragraph of the IoC-Container and the bold passages.

Dependency Injection

Dependency Injection is a design pattern, that does what the name states. It injects objects into the constructor or methods of other objects, so that one object depends on one or more other objects.

<?php  class DatabaseWriter {          protected $db;      public function __construct(DatabaseAdapter $db)     {         $this->db = $db;     }      public function write()     {         $this->db->query('...');     }  } 

You can see that we require the classes constructor a DatabaseAdapter instance to be passed. Since we are doing that in the constructor, an object of that class cannot be instantiated without it: We are injecting a dependency. Now, that we know that the DatabaseAdapter is always existent within the class we can easily rely on it.

The write() method just calls methods on the adapter, since we definitely know it exists because we made use of DI.

The enormous advantage of using DI instead of misusing static classes, god objects, and other stuff like that is, that you can easily track down where the dependencies come from.

The other huge advantage is, that you can easily swap out dependencies. If you want to use another implementation of your dependency just pass it to the constructor. You don't need to hunt down hardcoded instances anymore in order to be able to swap them.

Leaving aside the fact that by using Dependency Injection you will easily be able to Unit-Test your classes, since you can just mock the dependencies which is hardly possible with hardcoded dependencies.

The three types of Dependency Injection

Constructor Injection

The type of Dependency Injection explained above is called Constructor Injection. That simply means that dependencies are passed as arguments to the classes constructor. The dependencies are then stored as properties and thus made available in all methods of the class. The big advantage here is, that a object of the class cannot exist without passing the dependencies.

Setter Injection

This type makes use of dedicated methods to inject dependencies. Instead of using the constructor. The advantage of using Setter Injection is, that you can add dependencies to an object after it has been created. It's commonly used for optional dependencies. Setter Injection is also great to declutter your constructor and have your dependencies only in methods where you need them.

<?php  class RegisterUserService {      protected $logger;      public function setLogger( Logger $logger )     {         $this->logger = $logger;     }      public function registerUser()     {         // Do stuff to register the user         if($this->logger)             $this->logger->log("User has been registered");     }  }  $service = new RegisterUserService; $service->registerUser(); // Nothing is Logged  $service->setLogger(new ConcreteLogger); $service->registerUser(); // Now we log 

The object can be instantiated without any dependencies. There is a method to inject the dependency (setLogger()) which can be called optionally. Now it's up to the methods implementation to either make use of the dependency or not (if it's not set).

It's worth pointing out to be cautious with Setter Injection. Calling methods or accessing attributes on a dependency that has not yet been injected will result in a nasty Fatal error: Call to a member function XXX() on a non-object . Therefore everytime the dependency is accessed it has to be null-checked first. A much cleaner way to go about this would be to use the Null Object Pattern and to move the dependency into the constructor (as optional argument, if nothing is passed the null object is created inside the class)

Interface Injection

The idea of interface injection is basically, that the method(s) to inject a dependency is defined in an interface. The class that is going to need the dependency must implement the interface. Hereby it is ensured that the needed dependency can be properly injected into the dependent object. It's a stricter form of the previously explained Setter Injection.

<?php  interface Database {      public function query();  }  interface InjectDatabaseAccess {      // The user of this interface MUST provide     // a concrete of Database through this method     public function injectDatabase( Database $db );  }  class MySQL implements Database {      public function query($args)     {         // Execute Query     }  }  class DbDoer implements InjectDatabaseAccess {      protected $db;      public function injectDatabase( Database $db )     {         $this->db = $db;     }      public function doSomethingInDb($args)     {         $this->db->query();     }  }  $user = new DbDoer(); $user->injectDatabase( new MySQL ); $user->doSomethingInDb($stuff); 

The meaningfulness of Interface Injection is open to dispute. Personally I have never used it. I prefer Constructor Injection over it. That allows me to accomplish the exact same task without the need of additional interfaces on the injectors side.

Dependency Inversion

Instead of depending on a concrete instance we can also depend on abstractions.

<?php  class DatabaseWriter {          protected $db;      public function __construct(DatabaseAdapterInterface $db)     {         $this->db = $db;     }      public function write()     {         $this->db->query('...');     }  } 

Now we inverted the control. Instead of relying on a concrete instance, we can now inject any instance that consumes the type hinted interface. The interface takes care that the later concrete instance implements all the methods that we are going to use, so that we can still rely on them in the dependent classes.

Make sure you get that one, because it is a core concept of the IoC-Container.

The IoC Container

In the shortest terms I can think of I would describe the IoC container like that:

The IoC-Container is a component that knows how instances are created and knows of all their underlying dependencies and how to resolve them.

If we take our example above, imagine that the DatabaseAdapter itself has it's own dependencies.

class ConcreteDatabaseAdapter implements DatabaseAdapterInterface{      protected $driver;      public function __construct(DatabaseDriverInterface $driver)     {         $this->driver = $driver;     }  } 

So in order to be able to use the DatabaseAdapter you would need to pass in a instance of the DatabaseDriverInterface abstraction as a dependency.

But your DatabaseWriter class does not know about it, nor should it. The DatabaseWriter should not care about how the DatabaseAdapter works, it should only care about that a DatabaseAdapter is passed and not how it needs to be created. That's where the IoC-Container comes in handy.

App::bind('DatabaseWriter', function(){     return new DatabaseWriter(         new ConcreteDatabaseAdapter(new ConcreteDatabaseDriver)     ); }); 

As I already said, the DatabaseWriter itself does not know anything about the dependencies of it's dependencies. But the IoC-Container knows all about them and also knows where to find them. So eventually when the DatabaseWriter class is going to be instantiated, the IoC-Container first is asked how it needs to be instantiated. That's what the IoC-Container does.

Simply spoken (if you are into design patterns). It's a bit of a combination of a Dependency Injection Container (which I already explained above) and a Service Locator.

like image 197
thpl Avatar answered Oct 07 '22 03:10

thpl


consider you have a Car class like bellow :

    class Car      {          protected $wheels;         protected $engine;          public function __construct()         {             $this->engine = new Engine('BMW Engine');              $this->wheels  = array(                                new Wheel('some Wheel Brand'),                                new Wheel('some Wheel Brand'),                                new Wheel('some Wheel Brand'),                                new Wheel('some Wheel Brand')                               );         }          public function go()         {             //do fun stuff here         }    } 

now think about reusing your code and for example you want to have another car class which is for Toyota company. you should write exactly above code with just a little editing as below :

class AnotherCar  {      protected $wheels;     protected $engine;      public function __construct()     {         $this->engine = new Engine('Toyota Engine');          $this->wheels  = array(new Wheel('some Wheel Brand'),                            new Wheel('some Wheel Brand'),                            new Wheel('some Wheel Brand'),                            new Wheel('some Wheel Brand'));     }      public function go()     {         //do fun stuff here     }   } 

your code will grow and grow and will be a pain if you don't use DI(Dependency Injection) or IoC(Inversion Of Control).

lets talk about what are the above code problems : 1- its not reusable . 2- it doesn't care about performance (Volume of code in your Ram , ...) 3- your code grows and you will not understand it yourself after a time . ...

so lets use DI and solve the problem

class Car  {       protected $wheels;     protected $engine;      public function __construct($myWheels , $myEngine)     {        $this->engine = $myEngine;        $this->wheels  = $myWheels;      }      public function go()     {         //do fun stuff here     } } 

now easily you can create every car object with above Car class like below :

$BMW = new Car (new Engine('BMW' , array(new Wheel('wheel beand') ,new Wheel('wheel beand') ,new Wheel('wheel beand') ,new Wheel('wheel beand') ))); 

see ! much easier ! also your code is much readable and reusable !

lets consider you have a big project that have so many classes that probably lot of objects and classes depends on other objects and classes.

it will bother you to keep in mind while working that which class depends on what classes and ... this is where IOC comes in and solve the problems.

IOC pattern or maybe solution is not really easy to understand you should face to problems I mentioned earlier . IOC just is neede in big projects ! in java Spring is one of the most famous IOC container and what IOC does??? it contains lots of config file ( or some IOC container configs are in source code and also some of them have user interface to configs) and this configs contain relation between class and which class for loading depend on which classes this also is a big help for lazy loading ! just when you need to load an object you load its dependecies !

like image 35
Majid Abdolhosseini Avatar answered Oct 07 '22 04:10

Majid Abdolhosseini