cult3

Creating flexible Controllers in Laravel 4 using Repositories

Jul 08, 2013

Table of contents:

  1. An illustration of the problem
  2. Flexibility through Repositories
  3. What are Repositories?
  4. What do we need to build?
  5. Project structure
  6. Creating the User Repository
  7. Creating the Eloquent User Repository
  8. Creating the Service Provider
  9. Implementing the Repository in the Controller
  10. Setting up a route and testing in the browser
  11. Switching Repositories
  12. Conclusion

Last week I looked at setting up your first Controller in Laravel 4. Controllers are what dictate how data is transferred between your Models and Views and vice versa. We set up our first RESTful controller and I described what each of the methods should be used for.

This week’s tutorial is all about creating Controllers that are flexible. Your Controllers are going to be one of the key components of your application and so you need to ensure that you build them so that if future circumstances change you don’t have to completely rewrite them.

To make the Controllers flexible, I’m going to use Repositories to abstract the database layer away.

This might seem a little bit confusing, but stick with me, it will all make sense by the end of this post.

An illustration of the problem

So to really understand what I’m trying to achieve here, first I’ll give you an example of the situation I’m trying to avoid.

For example, we have the following index method in the UserController.php file:

/**
 * Display a listing of the resource.
 *
 * @return Response
 */
public function index()
{
    // Return all Users
    return User::all();
}

This method simply returns a list of all users from out application.

However, there is a problem with this:

It’s not flexible - User::all() is tied to Laravel’s Eloquent ORM. What would you do if you needed to switch databases to use Redis or Mongo instead? Well, you would have to find all of the instances that you used Eloquent in your application and update them. This isn’t very flexible in a larger application.

This might not seem like big problem, but trust me it is! You might be thinking, “hey, as long as it works, it’s all good right?”. Well building a solid application foundation is really not that hard and Laravel makes it really easy to solve both of these problems. There’s no need to tightly couple your application to one particular ORM or database if you don’t have to. You should seize any opportunity to build future flexibility into your application!

Flexibility through Repositories

In order to cleanly separate our Controllers from the database, we are going to abstract that interaction into repositories. A repository is simply an interface between two things.

So instead of referencing Eloquent directly, we can reference UserRepository. We can then bind UserRepository to EloquentUserRepository so that Laravel knows that whenever we mention UserRepository we want an instance of EloquentUserRepository.

Now that we have abstracted the database layer into repositories it makes it much easier to switch database ORM.

For example, if you wanted to use Mongo instead, you would simply create a MongoUserRepository and bind UserRepository to it rather than EloquentUserRepository.

Now whenever Laravel wants a UserRepository it will return MongoUserRepository.

This means that you don’t have to change any of the code in your Controllers!

What are Repositories?

As I mentioned above, Repositories are simply an interface between two things. You can think of them as a contract that states that certain methods will be made available when using this interface.

For example:

You might have the following UserRepository:

interface UserRepository {

    public function all() {}

    public function find() {}

}

And the following EloquentUserRepository:

class EloquentUserRepository implements UserRepository
{
    public function all()
    {
        return User::all();
    }

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

The UserRepository is just an interface for the EloquentUserRepository it doesn’t care what method of storing data is used, all it cares about is that those methods are available.

Now in your Controller you can use the UserRepository interface as an abstraction from the database layer.

So hopefully that all makes sense to you. We are basically just making ORM specific classes and then using an interface in the Controller as a way of talking to the database. The interface is simply the connection between the Controller and the Repository.

What do we need to build?

In order for our application to use Repositories, first we need to set things up.

We are going to need:

  • UserRepository
  • EloquentUserRepository
  • A way to bind UserRepository and EloquentUserRepository

If you remember back to building your first Laravel package, we can use Service Providers to bind things together. Service Providers are just like bootstrap classes that allow you to set things up in a certain way.

Project structure

As with a lot of things, you can get away with placing any of these things in a lot of random areas in your project. However, it’s pretty bad practice to just dump stuff in a random file or leave things in the wrong directory. As your project gets bigger, things will become a mess and everything will be unmaintainable.

Instead, I’m going to create a new directory called app/lib to store all of this kind of stuff.

In order for this directory to be included in the autoload, you also need to add it to your composer.json file.

In the classmap array, add your new directory:

{
    "autoload": {
    "classmap": [
        // —
        "app/lib"
    ]
}

Run the following command in Terminal to update your autoload classmap:

$ composer dump-autoload

Next, in the app/lib directory, I’m going to create another directory call Cribbb to keep all of the Cribbb specific things together.

Next, I’m going to create another directory under app/lib/Cribbb called Storage to keep all of my database repositories together. And finally, I’m going to separate each resource into its own directory.

So my final directory structure is: app/lib/Cribbb/Storage/User.

Creating the User Repository

The first thing to create is the UserRepository.php interface.

<?php namespace Cribbb\Storage\User;

interface UserRepository
{
    public function all();

    public function find($id);

    public function create($input);
}

Interfaces are extremely simple because all I’m doing here is declaring that these methods should be made available. In a future tutorial I will add the other methods I’m going to need to for updating or deleting, but for now we’ll keep it simple.

Creating the Eloquent User Repository

Next I will create the EloquentUserRepository.php file. Remember, this is simply an abstraction from the database that implements UserRepository:

<?php namespace Cribbb\Storage\User;

use User;

class EloquentUserRepository implements UserRepository
{
    public function all()
    {
        return User::all();
    }

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

    public function create($input)
    {
        return User::create($input);
    }
}

Creating the Service Provider

Next I need to create the Service Provider which will bind the two repositories together.

In app/lib/cribbb/storage create a new file called StorageServiceProvider.php, and copy the following code:

<?php namespace Cribbb\Storage;

use Illuminate\Support\ServiceProvider;

class StorageServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(
            "Cribbb\Storage\User\UserRepository",
            "Cribbb\Storage\User\EloquentUserRepository"
        );
    }
}

If you remember back to my article on creating a Laravel 4 Package, the register method is automatically called on the Service Provider. This allows you to bootstrap your files so everything is loaded correctly.

In this example, I’m binding the User Repository to the Eloquent User Repository. This means, whenever I want to use the User Repository, Laravel will automatically know that I want to use the Eloquent User Repository. If in the future I wanted to use Mongo instead, I would simply have to create a Mongo User Repository and update this binding.

Finally you need to make Laravel aware of this Service Provider by placing it in the providers array under app/config/app.php:

'providers' => array(
    // —
    'Cribbb\Storage\StorageServiceProvider'
),

Implementing the Repository in the Controller

Now we can start using the Repository in the User Controller. Open up UserController.php and add the use line to the top of your file:

use Cribbb\Storage\User\UserRepository as User;

class UserController extends BaseController
{
}

Next you need to create a __construct method that injects an instance of the User Repository into the Controller and sets it to the $this->user property:

public function __construct(User $user)
{
    $this->user = $user;
}

And finally, you can set the index method to return all of the users:

/**
 * Display a listing of the resource.
 *
 * @return Response
 */
public function index()
{
    return $this->user->all();
}

Setting up a route and testing in the browser

Now all we need to do to ensure that everything is working correctly is to set up a route to hit so we can see what is being returned in the browser.

Open up your routes.php file and copy the following to define a new route to the User Controller:

Route::resource("user", "UserController");

Now if you fire up the server and hit /user in your browser, you should see a JSON array displayed on the screen (as long as you actually have users in your database!).

Switching Repositories

Now if you ever need to switch ORM or database in the future, all you have to do is to create a new database Repository and update the bindings in your Service Provider.

For example, to switch to Mongo, all you would have to do is to copy the EloquentUserRepository.php and create MongoUserRepository.php. You would then replace the Eloquent code with Mongo code so that the same data is returned.

In your Service Provider, you would simply update the binding to use the Mongo repository instead of the Eloquent repository:

$this->app->bind(
    "Cribbb\Storage\User\UserRepository",
    "Cribbb\Storage\User\MongoUserRepository"
);

Now you have completely switched databases without having to change any of your Controller code!

Conclusion

Repositories allow you to create a flexible abstraction layer between your database and your Controller. Doing this enables you to separate those concerns and it prevents your Controllers being too tightly coupled with your Database.

Your Controllers don’t care what storage facility you are using to persist data, and so by using Repositories, you are able to make a clean abstraction.

This makes it beautifully simple to switch database types at some point in the future.

Next week I’m going to look at how you should structure your Controllers to make them easier to test. Again this is because your Controllers should only be concerned with their job and not what the database is doing.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.