What is a repository and how does it work?

When I first heard about the term "repository" in reference to code I was throughly confused. Prior to Laravel, the only place I had seen repositories were in the context of SVN or a GIT repo possibly. So what other context is there? What does this term mean in relation to code? A repository is a code abstraction whose purpose is to seperate the knowledge of how data is retrieved from the code that is actually doing the retrieving.

Repositories in a nutshell

To boil it down to simplest terms. Your code that is fetching data shouldn't be tied to one specific way of fetching data.

Your client code should have no knowledge of how data is fetched. It only needs to know how to issue the command to fetch the data.

Using a repository in your code is just a way in to delegate data management responsibilities to a more fitting area of code. It is primarily used as a way to make your code more loosely coupled to it's data layer and make it more cohesive.

You can think of repositories as simply a bridge between your client code and your data layer. Think of a bridge in real life that joins two cliffs. If you want to switch the data layer then it's as if one side of the bridge magically twists and connects to a different cliff. Now you have a whole new path but the old path is still there in case you want to switch back.

Repositories in action in Laravel

Enough with the banter. Let's take a look at what this looks like in Laravel. For this example, let's say you want to work with a group of users.

Dev 101 way:

//UsersController.php             
.....
public function index()  
{
    $users = User::all();
    return View::make('users.all', compact('users'));
}

public function show($id)  
{
     $user = User::find($id);
     return View::make('users.single', compact('user'));
}
.....

You might be thinking that looks fine, and you might be right. It all boils down to how loosely coupled your code needs to be to it's data and how many changes you are expecting in the future. In the code above, there are several problems:

  1. Your controller has intimate knoledge of how the data needs to be fetched. If changes are needed they are needed in many places.
  2. You are tying yourself to one specific method of fetching data. In this case, the eloquent ORM.

The Jedi Way

A better way to go about retrieving your data is to code to an abstraction. Pass in, or inject, your data container and then fetch and manipulate against that like so:

//UserRepositoryInterface.php
interface UserRepositoryInterface {  
    public function all();
    public function find();
}

//UserEloquentRepository.php
class UserEloquentRepository implements UserRepositoryInterface {

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

    public function find($uid)
    {
        return User::find($uid);
    }
    ....
}

//UsersController.php
<?php 

class UsersController extends BaseController{  
    public $repo;
    public function __construct(UserRepositoryInterface $repo)
     {
        $this->repo = $repo; 
     }

     public function index()
     {
         $users = $this->repo->all();
        return View::make('users.all', compact('users'));
     }

     public function find($id)
     {
         //...
     }
     ....
}

So Why is this better?

Well i'm glad you asked. Let me count the reasons:

  1. Your client code ( The UsersController ) is now free to use any data source that fits (File, Postgres, ect.. ). You are not tied to any specific implementation.

  2. Limited Knowledge: The client code has no idea what data source it's using, only that one is there. Like a child knowing his parents have money, they have the ability to get it, but not knowing how they do their jobs.

  3. This code is more testable. You can now inject mocks through automatic resolution or using the IoC container.

If the code above seems monsterous don't worry. Even though we blended a lot of concepts above if you keep reading and be tenacious I promise you'll get it. They aren't as scary as they seem.

Where did we get $repo?

I knew you were thinking it, if you weren't you should have been! ;) Think about it. The way our code is written, UsersController is just expected to automatically know what the correct value of $repo is. That would be understandable if the type hint that proceeds it was an actual concrete class, but it isn't. The type hint is an interface, an abtraction. If we were to run that code it would fail in it's current state.

Enter the IoC container and automatic resolution

Once you have setup your repository and your interface, you have to specidically tell Laravel which concrete class to use when you ask for an intance that implements that interface. You can do that in your routes.php file or in a bindings class that you stick in start.php or somewhere. Check out how:

//Abstraction / Concrete Class
App::bind('UserRepositoryInterface', 'UserEloquentRepository');  

Now whenever an instance of UserRepositoryInterface is asked for the IoC container will return a UserEloquentRepository. You could easily switch this one line binding and replace it with a file based approach or something else, implement the interface and BOOM!

Resouces

I hope this helped shed some light on how to decouple your data layer from your controllers and logic layer. If you have any questions look me up or leave a comment.

If you want to continue learning then a simple Google search should do the trick. Beyond that here are some great resources below to paruse.

Nettuts+ | The Repository Pattern
Laracasts | Repositories Simplified
The IoC container laravel.com
Symfony | Containers and Services