3. poo inyección de dependencias en laravel (iii)

Upload: sanbaucal

Post on 13-Jan-2016

15 views

Category:

Documents


0 download

DESCRIPTION

kjbkjbkj,bmnb

TRANSCRIPT

3.5 - POO: Inyeccin de dependencias en Laravel (III)Este es el tercer tutorial de la serie sobreinyeccin de dependencias. Antes de comenzar asegrate de haber ledoel primeroyel segundo tutorialde esta nueva entrega.

En la POO generalmente usamos el constructor de una clase para definir sus dependencias. Ejemplo:

Cdigo :class MysqlDatabase {

}

class UserRepo { /** * @var MysqlDatabase $db */ protected $db;

public function __construct(MysqlDatabase $db) { $this->db = $db; }}

class PostRepo { /** * @var MysqlDatabase $db */ protected $db;

public function __construct(MysqlDatabase $db) { $this->db = $db; }}

class PostsController {

protected $users; protected $posts;

public function __construct(UserRepo $users, PostRepo $posts) { $this->users = $users; $this->posts = $posts; }

}

UseRepo y PostRepo dependen de MysqlDatabase, y PostsController depende de UserRepo y PostRepo. Slo para crear un PostsController tendramos que hacer esto:

Cdigo :$db = new MysqlDatabase();$users = new UserRepo($db);$posts = new PostRepo($db);$controller = new PostsController($users, $posts);var_dump($controller);

Mucho trabajo, no? Bueno, uno de los features que ms me gusta de Laravel es que puede instanciar un objeto por nosotros, creando todas las dependencias que hagan falta,como les mostr en este artculo.Inyeccin de dependencias en Laravel

Laravel usa las clasesReflection de PHPen sucontenedorpara conocer qu dependencias tiene cada clase y las crea una a una, recursivamente (como un rbol de dependencias). Aunque parezca algo super complicado es tan fcil que voy a crearles un ejemplo en tan slo cinco minutos

Otros 30 minutos despus(Los programadores somos demasiado optimistas con los tiempos)

Veamos el cdigo y debajo los ejemplos:

Cdigo :class Factory {

/** * Store the alias between interfaces/parent classes and concrete/child classes * @var array */ protected $alias = array();

/** * Bind an alias to a real class name. i.e.: UserRepoInterface to UserRepo. * Can be use to replace parent classes with child classes. * (Be aware both classes would need the same interface). * @param $alias * @param $name */ public function bind($alias, $name) { $this->alias[$alias] = $name; }

/** * Get the real name used for a class, as indicated by the bind method above * Example: getRealName('UserRepoInterface') should return 'UserRepo' * @param $name * @return mixed */ public function getRealName($name) { if (isset ($this->alias[$name])) { $name = $this->alias[$name]; }

return $name; }

/** * Build a new object determines and instantiate its dependencies automatically * @param $name * @return object */ public function build($name) { try { $name = $this->getRealName($name);

$reflection = new ReflectionClass($name);

if ( ! $reflection->isInstantiable()) { throw new Exception($name . " is not instantiable"); }

$constructor = $reflection->getConstructor();

if (is_null($constructor)) { return new $name; }

$parameters = $constructor->getParameters();

$args = array();

foreach ($parameters as $parameter) { $args[] = $this->build($parameter->getClass()->getName()); }

return $reflection->newInstanceArgs($args); } catch(Exception $e) { exit('Error trying to build "' . $name . '": ' . $e->getMessage()); } }

}

Ahora para crear un PostsController slo hacemos esto:

Cdigo :$factory = new Factory;

$posts = $factory->build('PostsController');

var_dump($posts);

Vean de nuevo cmo la nueva clase Factory instancia por nosotros el controlador y sus dependencias y las dependencias de las dependencias: PostController -> UserRepo -> MysqlDatabase, etc.

Pero la mejor prctica es no atar una implementacin a una clase concreta sino usar interfaces, veamos esto ltimo. Primero las clases e interfaces dummies para crear el ejemplo:

Cdigo :interface DatabaseInterface {

}

class MysqlDatabase implements DatabaseInterface {

}

interface UserRepoInterface {

}

interface PostRepoInterface {

}

class UserRepo implements UserRepoInterface { /** * @var DatabaseInterface $db */ protected $db;

public function __construct(DatabaseInterface $db) { $this->db = $db; }}

class PostRepo implements PostRepoInterface { /** * @var DatabaseInterface $db */ protected $db;

public function __construct(DatabaseInterface $db) { $this->db = $db; }}

class PostsController {

protected $users; protected $posts;

public function __construct(UserRepoInterface $users, PostRepoInterface $posts) { $this->users = $users; $this->posts = $posts; }

}

Noten que PostsController ahora necesita "UserRepoInterface" en vez de "UserRepo" y UserRepo necesita de DatabaseInterface en vez de MysqlDatabase, esto hace nuestras clases mucho ms flexibles (podramos tener diferentes repositorios y bases de datos), pero si ahora procedemos como antes:

Cdigo :$factory = new Factory;

$controller = $factory->build('PostsController');var_dump($controller);

Boom!Error trying to build "UserRepoInterface": UserRepoInterface is not instantiablePor qu? No se puede instanciar una interface, y la clase Factory no sabe qu hacer. Ac s necesita instrucciones adicionales pero es muy fcil:

Cdigo :$factory = new Factory;

$factory->bind('DatabaseInterface', 'MysqlDatabase');$factory->bind('UserRepoInterface', 'UserRepo');$factory->bind('PostRepoInterface', 'PostRepo');

$controller = $factory->build('PostsController');

var_dump($controller);

Laravel hace esto, al instanciar controladores usa elIoCque permite instanciar de manera automtica todas las dependencias. Similar a lo que programamos aqu.

Por ltimo quiero aclarar que mi clase es slo con fines educativos, hara falta integrarla con el Container del artculo pasado, crear Unit Tests, etc. Mi intencin en este tutorial es como el mago que revela los trucos, al entender cmo funcionan los frameworks por dentro se hacen mejores programadores y la prxima vez que alguien les hable de Dependency injection y Automatic resolution no se sentirn intimidados.

Te gust el tutorial? Nos vemos en el prximo! Saludos.