Laravel 5: Repositories

Laravel 5: Repositories


Repositories help to make your app future-proof and testable, by loosely coupling your controllers to database logic, e.g. Eloquent, so you can switch your data storage anytime without affecting rest of your app.

A few words to start with …

When you build your application, you choose how you handle your data.
Most likely you are going to choose MySQL and manage it via Query builder, or Eloquent ORM.
It is extremely unlikely, you’d ever change this.
But what, if you’d choose to switch to NoSQL Mongo, or some other file based storage?
You’d be in a world of hurt … and a big time.
Unless, you made a right choice to begin with and chose to use Interface powered Repositories.
If you did, you can start celebrating the right choice, as switch will be easy.

I will do step by step, how to get repository going and how to use loose coupling between data managing solution, e.g. Eloquent and place, where you use fetched data e.g. Controller.
step 1: create repository folder

If you do not have it yet, lets create Repositories folder (directory) inside of /app.
Our Repository folder will hold all our repositories.
If you design some huge app, you may consider sub-folders inside, so you will not end up with 50 or more repository class files one under another.

step 2: create repository class

Now, lets create our repository class.
Since we do not have any Artisan shortcut, we have to do it by hand.
Lets select some naming convention for our repositories and try to stick to it throughout our whole app.
Lets call our repo: DbUserRepository.
So, all my repositories will have Db prefix.

Here is my class – it will be still adjusted below in step 3:

I have 2 simple methods:

  • get all users
  • get selected user by id
step 3: create Interface for Repository

I could call this Repository directly in Controller, but then the whole repository concept would be manhandled.

Basically, we move database logic out of Controller to allow for a loose coupling, so later on, if for any reason we want to change database storage engine, e.g. to NoSQL Mongo, we can do this without major app rework.

And for that loose coupling we need interface.

There is nothing to it.

create Interfaces directory

If you do not have Interface folder yet, I suggest creating it in /app directory.
Since Interface is a sort of contract, you could call this folder Contracts as well.

create interface class

Lets create UserRepositoryInterface inside of Interfaces folder.

Interface not only connects to proper repository, but being a contract, it also forces us to include all listed methods in any new implementation we create for a different database engine, e.g. for Mongo – if we decide to switch to it.

But we are not done yet.
Our DbUserRepository does not know yet, that it is linked to our UserRepositoryInterface.

All we have to do is to implement it, like this:

Above line comes from repository.
Full code of repository is a bit above on this page.

step 4: binding interface to repository

So, we have our DbUserRepository implementing UserRepositoryInterface, but Laravel Container – place responsible for injecting dependencies – still is unaware of connection between DbUserRepository and UserRepositoryInterface.
Lets change that.

Best place to talk to Laravel’s Container is through some ServiceProvider.
We could create a dedicated one, but I think that existing AppServiceProvider is just the right place.

We will bind Interface to Repository in method register():

Now, Laravel knows all it needs to and we can use repository to fetch data in Controller.

step 5: use repository in controller

Lets say I will use HomeController accessible via domain.com/home route:

And this is my HomeController:

I am asking for a use with id: 1, and here what I get:

So, as you see, all works as intended

Summary

Now, if I want to switch to a new data engine, all I have to do is to write another repository and change binding in ServiceProvider to that new repository.
Everything else in my app will work without a hiccup.

All files from steps above in full
DbUserRepository

UserRepositoryInterface

AppServiceProvider

HomeController

We are done …