Laravel and repositories: introduction and tutorial

What are repositories?

Using repositories, we can abstract the database layer away from the controller.

So what does this mean? This means that we can use the Eloquent ORM and build our code using that. Then, is for some reason you decide to move to say MongoDB, we only have to change 1 line of code per model:

App::bind('PostsRepositoryInterface', 'EloquentPostsRepository');    

This code tells Laravel that whenever the PostsRepositoryInterface is called, it actually wants to use the EloquentPostsRepository. So, sticking with our example, if we wanted to change to MongoDB, we would change this to:

App::bind('PostsRepositoryInterface', 'MongoPostsRepository');    

How easy is that?

Another reason we'd use repositories is for testing. When you're testing your application, you don't care what the database returns, as long as it returns something. So, using mockery and PHPUnit, we can mock the repository and just return one constant value.

$mock = Mockery::mock('PostsRepositoryInterface');
$mock->shouldReceive('all')->once()->andReturn(array('title' => 'post', 'content' => 'post body'));
App::instance('PostsRepositoryInterface', $mock);    

We can switch out what PostsRepositoryInterface is before we run our tests!

So now hopefully, you can see how awesome repositories are. It took me a few days to figure everything out after watching a few YouTube videos, a lot of reading and even more frustration. So, let's dive straight in...

How do I create repositories?

First off, we're going to need a model.

class Posts extends Eloquent {
    protected $table = 'posts';
}

So far so good. Now, let us create the EloquentPostsRepository. As with everything in Laravel, you can chuck it wherever you like. I prefer to chuck everything into an app/repositories folder and add app_path().'/repositories' to my app/start/global.php file.

class EloquentPostsRepository implements PostsRepositoryInterface {
protected $model;

    public function __construct(Posts $posts) {
        $this->model = $posts;
    }

    // we'll talk about this in a bit
    public function all() {
        $this->model->all();
    }
}

Laravel is clever enough to know to auto-load the Posts Model. How sweet is that? We need to create the interface now.

class PostsRepositoryInterface {
    public function all();
}

Why do we need an interface do you ask? This is to ensure that if you do swap from Eloquent to Mongo, the same functions have to exist, otherwise PHP will just stop you.

We need to tell Laravel to use the repository. So, we use App::bind. I chuck all my bindings in a bindings.php file, and have a require in my routes file.

App::bind('PostsRepositoryInterface', 'EloquentPostsRepository');

Still with me?

So how the hell do we actually use this in a controller?

class PostsController extends BaseController {
    protected $posts;

    public function __construct(PostsRepositoryInterface $posts) {
        $this->posts = $posts;
    }

    public function getAll() {
        return $this->posts->all();
    }
}

There. Done and done. Pretty simple and awesomely powerful.

That is pretty sweet, but explain the functions in the repository?

Right. The repository is a wrapper for Eloquent. As you would call Posts::all() or Posts->all() we now call $this->model->all(). So, for an example

public function getPostsByCategoryId($id) {
    return $this->model->where('id', '=', $id)->get();
}

Easy as pie. Let me know if I've missed anything or there is a typo somewhere.

A big shout out to @jeffrey_way for his awesome videos and just being a generally cool dude.