Managing relationships in Laravel, adhering to the repository pattern Managing relationships in Laravel, adhering to the repository pattern laravel laravel

Managing relationships in Laravel, adhering to the repository pattern


I am finishing up a large project using Laravel 4 and had to answer all of the questions you are asking right now. After reading all of the available Laravel books over at Leanpub, and tons of Googling, I came up with the following structure.

  1. One Eloquent Model class per datable table
  2. One Repository class per Eloquent Model
  3. A Service class that may communicate between multiple Repository classes.

So let's say I'm building a movie database. I would have at least the following following Eloquent Model classes:

  • Movie
  • Studio
  • Director
  • Actor
  • Review

A repository class would encapsulate each Eloquent Model class and be responsible for CRUD operations on the database. The repository classes might look like this:

  • MovieRepository
  • StudioRepository
  • DirectorRepository
  • ActorRepository
  • ReviewRepository

Each repository class would extend a BaseRepository class which implements the following interface:

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);}

A Service class is used to glue multiple repositories together and contains the real "business logic" of the application. Controllers only communicate with Service classes for Create, Update and Delete actions.

So when I want to create a new Movie record in the database, my MovieController class might have the following methods:

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.}

It's up to you to determine how you POST data to your controllers, but let's say the data returned by Input::all() in the postCreate() method looks something like this:

$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')

Since the MovieRepository shouldn't know how to create Actor, Director or Studio records in the database, we'll use our MovieService class, which might look something like this:

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;}

So what we're left with is a nice, sensible separation of concerns. Repositories are only aware of the Eloquent model they insert and retrieve from the database. Controllers don't care about repositories, they just hand off the data they collect from the user and pass it to the appropriate service. The service doesn't care how the data it receives is saved to the database, it just hands off the relevant data it was given by the controller to the appropriate repositories.


Keep in mind you're asking for opinions :D

Here's mine:

TL;DR: Yes, that's fine.

You're doing fine!

I do exactly what you are doing often and find it works great.

I often, however, organize repositories around business logic instead of having a repo-per-table. This is useful as it's a point of view centered around how your application should solve your "business problem".

A Course is a "entity", with attributes (title, id, etc) and even other entities (Assignments, which have their own attributes and possibly entities).

Your "Course" repository should be able to return a Course and the Courses' attributes/Assignments (including Assignment).

You can accomplish that with Eloquent, luckily.

(I often end up with a repository per table, but some repositories are used much more than others, and so have many more methods. Your "courses" repository may be much more full-featured than your Assignments repository, for instance, if your application centers more around Courses and less about a Courses' collection of Assignments).

The tricky part

I often use repositories inside of my repositories in order to do some database actions.

Any repository which implements Eloquent in order to handle data will likely return Eloquent models. In that light, it's fine if your Course model uses built-in relationships in order to retrieve or save Assignments (or any other use case). Our "implementation" is built around Eloquent.

From a practical point of view, this makes sense. We're unlikely to change data sources to something Eloquent can't handle (to a non-sql data source).

ORMS

The trickiest part of this setup, for me at least, is determing if Eloquent is actually helping or harming us. ORMs are a tricky subject, because while they help us greatly from a practical point of view, they also couple your "business logic entities" code with the code doing the data retrieval.

This sort of muddles up whether your repository's responsibility is actually for handling data or handling the retrieval / update of entities (business domain entities).

Furthermore, they act as the very objects you pass to your views. If you later have to get away from using Eloquent models in a repository, you'll need to make sure the variables passed to your views behave in the same way or have the same methods available, otherwise changing your data sources will roll into changing your views, and you've (partially) lost the purpose of abstracting your logic out to repositories in the first place - the maintainability of your project goes down as.

Anyway, these are somewhat incomplete thoughts. They are, as stated, merely my opinion, which happens to be the result of reading Domain Driven Design and watching videos like "uncle bob's" keynote at Ruby Midwest within the last year.


I like to think of it in terms of what my code is doing and what it is responsible for, rather than "right or wrong". This is how I break apart my responsibilities:

  • Controllers are the HTTP layer and route requests through to the underlying apis (aka, it controls the flow)
  • Models represent the database schema, and tell the application what the data looks like, what relationships it may have, as well as any global attributes that may be necessary (such as a name method for returning a concatenated first and last name)
  • Repositories represent the more complex queries and interactions with the models (I don't do any queries on model methods).
  • Search engines - classes that help me build complex search queries.

With this in mind, it makes sense every time to use a repository (whether you create interfaces.etc. is a whole other topic). I like this approach, because it means I know exactly where to go when I'm needing to do certain work.

I also tend to build a base repository, usually an abstract class which defines the main defaults - basically CRUD operations, and then each child can just extend and add methods as necessary, or overload the defaults. Injecting your model also helps this pattern to be quite robust.