Laravel & Repository Pattern

Cómo y por qué implementar Repository Pattern

Germán Lena / @german_lena

¿Cómo suelen ser nuestros controllers?

								
class UserController extends BaseController {

  public function getAllUsers()
  {
    $users = User::all();
	
	// Un poco de magia, claramente no suelta en el controller...
	// (Abstraction, Command bus, ...)
	
    return View::make('user.index', compact('users'));
  }

}
								
							

Problemas

  • Lógica de acceso a datos en el controller
  • Acceso a datos desparramado por toda la app
  • Nuestro controller esta altamente acoplado a la clase User
  • Es dificil de hacer tests

Ok, ¿cómo se soluciona?

¡Sacando esta lógica del controller!

Repository Pattern

Es simplemente una capa de abstracción donde va a parar toda la lógica de acceso a datos.

Entonces, hagamos el UserRepository

										
class UserRepository {

  public function getAllUsers()
  {
    return User::all();
  }

}
										
								

Actualicemos el controller

										
class UserController extends BaseController {

  public function getAllUsers()
  {
    $repo = new UserRepository;
    $users = $repo->getAllUsers();

    // Un poco de magia, claramente no suelta en el controller...
    // (Abstraction, Command bus, ...)

    return View::make('user.index', compact('users'));
  }

}
										
								

Inyectemos!

										
class UserController extends BaseController {

  public function __controller(UserRepository $repo)
  {
    $this->repo = $repo;
  }

  public function getAllUsers()
  {
    $users = $this->repo->getAllUsers();
	
    // Un poco de magia, claramente no suelta en el controller...
    // (Abstraction, Command bus, ...)
	
    return View::make('user.index', compact('users'));
  }
}
										
								

Y ahora?

  • El motor de IoC (que Laravel lo implementa con el inyector de dependencias) va a intentar instanciar la clase automágicamente
  • Centralizamos el acceso a datos
  • El controller sigue dependiendo de la clase User

¿Por qué abstraerlo totalmente?

  • No tenemos queries desparramadas
  • Intercambiar facilmente cómo accedemos a los datos
  • SOLID

¿SOLID?

Acrónimo mnemónico introducido por Uncle Bob (Robert C. Martin) que representa los cinco principios básicos de la programación orientada a objetos y el diseño.

  • Single responsibility principle: una clase tiene que tener una única responsabilidad
  • Open/closed principle: open for extension, close for modification
  • Liskov substitution principle: los objetos deben poder ser reemplazados por instancias de sus subtipos sin alterar el programa
  • Interface segregation principle: muchas interfaces específicas del cliente son mejores que una única interfaz de proposito general
  • Dependency inversion principle: depender de abstracciones y no de cosas concretas

¡Hagamos una interfaz!

Ok, la interfaz...

										
class IUserRepository {

  public function getAllUsers();
  public function getById();
  public function save(User user);

}

//----- e implementemos
class UserRepository implements IUserRepository {

//----- actualicemos el controller
class UserController extends BaseController {

  public function __construct(IUserRepository $user)
  {
    $this->user = $user;
  }
										
								

Genial, pero no me dijiste para qué!

¡Para aprovechar el IoC de Laravel!

De esta forma abstraemos completamente el controller de cómo traer los datos

Me dejo de funcionar el IoC...

2 formas para decirle como hacer el binding

										
App::bind('IUserRepository', 'UserRepository');
										
								

Lo podemos agregar en el globa.php o creando un archivo nuevo, por ej. app_bindings.php y agregando equire '/app_bindings.php'; al global.php

La otra

										
use Illuminate\Support\ServiceProvider;
class UserServiceProvider extends ServiceProvider {
  public function register()
  {
    $this->app::bind('Repositories\\User\\IUserRepository',
            'Repositories\\User\\UserRepository');
  }
}
										
								
										
// app/config/app.php
...
'providers' => array(
  ...
  'Repositories\User\UserServiceProvider'
  ...
)
...
										
								

Ok, resumime chabon!

¡Cambió la capa de datos (migramos a mongodb, redis o agregamos solr/elastic search ), solo hay que actualizar los repositorios!

Se agregó manejo de estado de los usuarios, tenemos que actualizar las queries en los repos que correspondan

FIN

Referencias

  • http://laravel.com/docs/ioc
  • http://fideloper.com/how-we-code
  • http://heera.it/laravel-repository-pattern
  • http://ryantablada.com/post/the-repository-pattern-in-action
  • http://culttt.com/2013/07/08/creating-flexible-controllers-in-laravel-4-using-repositories/
  • http://www.slashnode.com/reusable-repository-design-in-laravel/