Estoy terminando un gran proyecto usando Laravel 4 y tuve que responder todas las preguntas que me está haciendo en este momento. Después de leer todos los libros de Laravel disponibles en Leanpub y toneladas de Google, se me ocurrió la siguiente estructura.
- Una clase de modelo Eloquent por tabla datable
- Una clase de repositorio por modelo elocuente
- Una clase de servicio que puede comunicarse entre varias clases de repositorio.
Digamos que estoy construyendo una base de datos de películas. Tendría al menos las siguientes clases de Eloquent Model:
- Película
- Estudio
- Director
- Actor
- revisión
Una clase de repositorio encapsularía cada clase de Eloquent Model y sería responsable de las operaciones CRUD en la base de datos. Las clases del repositorio podrían verse así:
- MovieRepository
- StudioRepository
- DirectorRepositorio
- ActorRepositorio
- ReviewRepository
Cada clase de repositorio ampliaría una clase BaseRepository que implementa la siguiente interfaz:
interface BaseRepositoryInterface
{
public function errors();
public function all(array $related = null);
public function get($id, array $related = null);
public function getWhere($column, $value, array $related = null);
public function getRecent($limit, array $related = null);
public function create(array $data);
public function update(array $data);
public function delete($id);
public function deleteWhere($column, $value);
}
Una clase de servicio se utiliza para unir varios repositorios y contiene la "lógica empresarial" real de la aplicación. Los controladores solo se comunican con las clases de servicio para las acciones de creación, actualización y eliminación.
Entonces, cuando quiero crear un nuevo registro de película en la base de datos, mi clase MovieController puede tener los siguientes métodos:
public function __construct(MovieRepositoryInterface $movieRepository, MovieServiceInterface $movieService)
{
$this->movieRepository = $movieRepository;
$this->movieService = $movieService;
}
public function postCreate()
{
if( ! $this->movieService->create(Input::all()))
{
return Redirect::back()->withErrors($this->movieService->errors())->withInput();
}
// New movie was saved successfully. Do whatever you need to do here.
}
Depende de usted determinar cómo envía los datos a sus controladores, pero digamos que los datos devueltos por Input :: all () en el método postCreate () se ven así:
$data = array(
'movie' => array(
'title' => 'Iron Eagle',
'year' => '1986',
'synopsis' => 'When Doug\'s father, an Air Force Pilot, is shot down by MiGs belonging to a radical Middle Eastern state, no one seems able to get him out. Doug finds Chappy, an Air Force Colonel who is intrigued by the idea of sending in two fighters piloted by himself and Doug to rescue Doug\'s father after bombing the MiG base.'
),
'actors' => array(
0 => 'Louis Gossett Jr.',
1 => 'Jason Gedrick',
2 => 'Larry B. Scott'
),
'director' => 'Sidney J. Furie',
'studio' => 'TriStar Pictures'
)
Dado que MovieRepository no debería saber cómo crear registros de Actor, Director o Studio en la base de datos, usaremos nuestra clase MovieService, que podría verse así:
public function __construct(MovieRepositoryInterface $movieRepository, ActorRepositoryInterface $actorRepository, DirectorRepositoryInterface $directorRepository, StudioRepositoryInterface $studioRepository)
{
$this->movieRepository = $movieRepository;
$this->actorRepository = $actorRepository;
$this->directorRepository = $directorRepository;
$this->studioRepository = $studioRepository;
}
public function create(array $input)
{
$movieData = $input['movie'];
$actorsData = $input['actors'];
$directorData = $input['director'];
$studioData = $input['studio'];
// In a more complete example you would probably want to implement database transactions and perform input validation using the Laravel Validator class here.
// Create the new movie record
$movie = $this->movieRepository->create($movieData);
// Create the new actor records and associate them with the movie record
foreach($actors as $actor)
{
$actorModel = $this->actorRepository->create($actor);
$movie->actors()->save($actorModel);
}
// Create the director record and associate it with the movie record
$director = $this->directorRepository->create($directorData);
$director->movies()->associate($movie);
// Create the studio record and associate it with the movie record
$studio = $this->studioRepository->create($studioData);
$studio->movies()->associate($movie);
// Assume everything worked. In the real world you'll need to implement checks.
return true;
}
Así que lo que nos queda es una separación de preocupaciones agradable y sensata. Los repositorios solo conocen el modelo Eloquent que insertan y recuperan de la base de datos. Los controladores no se preocupan por los repositorios, simplemente entregan los datos que recopilan del usuario y los pasan al servicio apropiado. Al servicio no le importa cómo se guardan los datos que recibe en la base de datos, simplemente entrega los datos relevantes que le dio el controlador a los repositorios apropiados.