Deric Cain

Tech ramblings of a true #geek.


Using the Repository Pattern in Laravel 5

March 10th, 2016


The Repository Pattern is a very useful pattern with a couple of great uses. The first use is the abstraction that it provides. Meaning, it adds another layer between your application logic and your database. When tackling separation of concerns, this is very important. Another benefit is how easy it makes it to swap out your backend technology. For instance, let’s say you are using MySQL and want to change to MongoDB. Since the Repository Pattern uses interfaces as contracts, your application logic will remain the same and all you have to do is change out the repository. Sounds easy, right? Let’s dive in with a simple example.

Creating the Repository Interface

If you don’t know what interfaces are, have a look here before you go any further. We need an interface to act like a contract for our repositories. What do I mean by contract? Just like a written contract which descibes the confines of a specific obligation, an interface is the same for our code. It says that any class implementing the interface must perform specific actions. Or, in our case, must contain specific methods.

I will use the ever-popular blog scenario and create a post repository, as well as a comment respository. So, create a folder inside of the app folder and name it Repositories. Then, create a file and name it PostRepositoryInterface.html"php"><?php namespace App\Repositories; interface PostRepositoryInterface { }

Now, we will need to the contracts, or methods, that we want our PostRepository to implement. It will looks something like this:

<?php 

namespace App\Repositories;

interface PostRepositoryInterface
{
    /**
     * Get's a post by it's ID
     *
     * @param int
     */
    public function get($post_id);

    /**
     * Get's all posts.
     *
     * @return mixed
     */
    public function all();

    /**
     * Deletes a post.
     *
     * @param int
     */
    public function delete($post_id);

    /**
     * Updates a post.
     *
     * @param int
     * @param array
     */
    public function update($post_id, array $post_data);
}

Notice how there are not opening and closing curly braces. That is because no logic is stored in the interface - only the methods that we want our class to implement.

Creating the Repository

Now, we need to create our class. Within the same folder, Repositories, create a file and name it PostRepository.html"php"><?php namespace App\Repositories; class PostRepository implements PostRepositoryInterface { }

We must create the methods that declared in our interface. If we don’t, we will get an error at runtime and we will not be able to use our class. We will want to go ahead and add the logic that we will use to work with out posts. For this, I will include our Eloquent model Post.

<?php

namespace App\Repositories;

use App\Post;

class PostRepository implements PostRepositoryInterface
{
    /**
     * Get's a post by it's ID
     *
     * @param int
     * @return collection
     */
    public function get($post_id)
    {
        return Post::find($post_id);
    }

    /**
     * Get's all posts.
     *
     * @return mixed
     */
    public function all()
    {
        return Post::all();  
    }

    /**
     * Deletes a post.
     *
     * @param int
     */
    public function delete($post_id)
    {
        Post::destroy($post_id)  
    }

    /**
     * Updates a post.
     *
     * @param int
     * @param array
     */
    public function update($post_id, array $post_data)
    {
        Post::find($post_id)->update($post_data);
    }
}

Our class is now happy because it is using all of the methods defined by our interface. Now that we have our class and our interface created, be need to register our repository with Laravel’s container.

Registering our Repository with Laravel’s IoC Container

Create a file called BackendServiceProvider.html"php"><?php namespace App\Repositories; use Illuminate\Support\ServiceProvider; class BackendServiceProvider extends ServiceProvider { public function register() { $this->app->bind( 'App\Repositories\PostRepositoryInterface', 'App\Repositories\PostRepository' ); } }

The one thing to notice here is the order in which the interface and the class are bound. If you try and bind 'App\Repositories\PostRepository' before 'App\Repositories\PostRepositoryInterface', you will get an error. You must bind the interface first.

Still with me?

Just to make sure you’re following along, you should have a folder structure like this:

- app
--- Repositories
------ BackendServiceProvider.html"php"><?php

namespace App\Http\Controllers;

use App\Http\Requests;
use App\Repositories\PostRepositoryInterface;

class PostController extends Controller
{

    protected $post;

    /**
     * PostController constructor.
     *
     * @param PostRepositoryInterface $post
     */
    public function __construct(PostRepositoryInterface $post)
    {
        $this->post = $post;
    }

    /**
     * List all posts.
     *
     * @return mixed
     */
    public function index()
    {
        $data = [
            'posts' => $this->post->all()
        ];

        return view('templates.posts', $data)
    }

}

The first thing that we do is inject our PostRepositoryInterface into our constructor. The, we set our $post instance variable to an instance of our PostRepository object through our interface. This allows us to call the methods in our PostRepository class like we did in the index() method. Now we can do this:

$this->post->update($data_array);
$this->post->delete($post_id);
$this->post->get($post_id);

This keeps us from directly accessing our model like Post::find$id); which adds another layer of abstraction to our application. Don’t forget, should we stop using MySQL and start using MongoDB, or some other backend technology, all we have to do is swap out our PostRepository logic. This works because we are accessing the repository through our interface. This means that the code in our controller would not change at all. That’s a big win!

More than one Repository? Sure.

So, you need to create another repository because just having one repository is lame. Right? All you need to do is create an interface and then create a repository. Then, register them with Laravel’s IoC Container in our backend service provider file. I’ll use a Comment model to show you how.

Our directory would now look like this:

app

- Repositories
--- BackendServiceProvider.html"php"><?php 

namespace App\Repositories;

interface CommentRepositoryInterface
{
    // Define all methods here but remember not to use curly braces.
    public function all(); // Like this..
}

And our CommentRepository.html"php"><?php namespace App\Repositories; use App\Comment; class CommentRepository implements CommentRepositoryInterface { // Must use all methods that were defined in the CommentRepositoryInterface here public function all() { return Comment::all(); } }

The last thing that you will need to do is register it with the IoC Container in our BackendServiceProvider.html"php"><?php namespace App\Repositories; use Illuminate\Support\ServiceProvider; class BackendServiceProvider extends ServiceProvider { public function register() { $this->app->bind( 'App\Repositories\PostRepositoryInterface', 'App\Repositories\PostRepository' ); $this->app->bind( 'App\Repositories\CommentRepositoryInterface', 'App\Repositories\CommentRepository' ); } }

Again, pay attention to the order in which you list your interface and class.

Finished. Doesn’t that feel good?

You should have a fully functional repository design pattern implemented with Laravel 5. If you have any questions or comments, follow me up on Twitter!


blog comments powered by Disqus