study-group-05
Created by Juan Manuel Torres / @onema / onema.io
Follow Live
http://slides.com/onema/dependency-injection/live
Dependency injection menas providing objects the varaibles it needs instead of having it construct them. [1] [2]
It's a very useful technique for testing, since it allows dependencies to be mocked or stubbed out. [1]
Remember our "Hello World" Translation controller?
If we would have been lazy we could have done it like this:
class HelloWorldController {
private $request;
public function __construct() {
$this->request = Request::createFromGlobals();
}
public function translateAction() {
$language =
$this->request->attributes->get('lang');
// . . .
return $response;
}
}
We are creating object dependencies in the constructor. Hard to customize
$controller = HelloWorldController();
$response = $controller->translateAction();
Very easy to use
class HelloWorldController {
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function translateAction() {
$language =
$this->request->attributes->get('lang');
// . . .
return $response;
}
}
We inject the Request dependency via the constructor. Easy to customize
$request = Request::createFromGlobals();
$controller = HelloWorldController($request);
$response = $controller->translateAction();
Not so easy to use
We could use Globals in order to make any modifications to the request.
class HelloWorldController {
private $request;
public function __construct() {
$this->request =
Request::createFromGlobals();
global $attr;
if (isset($attr)) {
$request->attributes->add($attr);
}
}
// . . .
}
$attr = ['default_language' => 'EN']
$controller = HelloWorldController();
$response = $controller->translateAction();
Bad global, BAD!
We could modify the request by passing custom attributes array.
class HelloWorldController {
private $request;
public function __construct($attr = null)
{
$this->request =
Request::createFromGlobals();
if (isset($attr)) {
$request->attributes->add($attr);
}
}
// . . .
}
$attr = ['default_language' => 'EN']
$controller = HelloWorldController($attr);
$response = $controller->translateAction();
Is OK, but not a great solution
We could use a global registry.
class HelloWorldController {
private $request;
public function __construct()
{
$this->request =
Registry::get('request');
}
// . . .
}
Registry::set('request', new CustomRequest)
$controller = HelloWorldController();
$response = $controller->translateAction();
Now we depend on
the Registry
class HelloWorldController {
private $request;
public function __construct($request)
{
$this->request = $request;
}
// . . .
}
$request = Request::createFromGlobals();
$request->attributes->add($parameters);
$controller = HelloWorldController($request);
$response = $controller->translateAction();
$request = Request::createFromGlobals();
$request->attributes->add($parameters);
$controller = HelloWorldController($request);
$response = $controller->translateAction();
$controller = HelloWorldController();
$response = $controller->translateAction();
This is harder:
Than this:
class HelloWorldControllerTest extends PHPUnit_Framework_TestCase
{
public function testTranslation()
{
// Create a Mock Object for the Request class
$mockRequest = $this->getMock('Request');
$mockRequest->attributes->add(['lang' => 'EN']);
$controller = new HelloWorldController($mockRequest);
$response = $controller->translateAction();
$this->assertEquals('Hello World!',
$response->getContent(),
'Invalid English message.')
}
}
We use a setter instead of using the constructor
class HelloWorldController {
private $request;
public function __construct()
{
// . . .
}
public function setRequest($request)
{
$this->request = $request;
}
// . . .
}
$request = Request::createFromGlobals();
$controller = HelloWorldController();
$controller->setRequest($request);
$response = $controller->translateAction();
We set the class property directly, this requires the property to be public
class HelloWorldController {
public $request;
public function __construct()
{
// . . .
}
// . . .
}
$request = Request::createFromGlobals();
$controller = HelloWorldController();
$controller->request = $request;
$response = $controller->translateAction();
A Service Container (or dependency injection container) is simply a PHP object that manages the instantiation of services (i.e. objects).
A proper solution to simplify the creation of complex objects is to use a Dependency Injection Container
class Container
{
public function getRequest()
{
$request=Request::createFromGlobals();
$request->attributes->add([
'lang' => 'EN'
]);
return $request;
}
public function getHelloController()
{
$request = $this->getRequest();
return HelloWorldController($request);
}
}
$container = new Container();
$controller = $container->getHelloController();
$response = $controller->translateAction();
BY Juan Manuel Torres / onema.io / @onema / kinojman@gmail.com