A Pattern for Reusable Repository Design in Laravel

I like the repository pattern (link), but find it tedious in a lot of ways. We employ the pattern because it enables us to break the dependency between our controllers and/or service layer code and the ORM (typically Eloquent). Unfortunately, it usually involves writing a lot of redundant boilerplate code.

While working on a new project, I decided to work on a (partial) solution to my repository pattern woes. I used a combination of polymorphism, inheritance and dynamic programming to leverage the good parts of the pattern while limiting the boilerplate.

Overview

The basic idea of the pattern is to create repositories that implement a standard RepositoryInterface which covers the standard Eloquent methods in a non-static, non-facade driven way. This means we can use dependency inversion, increasing the testibility of our application. Also, because our data-layer code will be hidden behind repositories, we can isolate changes to queries and/or business logic to this layer.

But only implementing the Eloquent-specific functions is a little restrictive. I also wanted to add the ability for repositories to implement their own model-specific functions. In order to do this, we provide a model-specific interface for each repository, into which we can insert the model specific functions.

Finally, all the boilerplate is hidden behind an AbstractRepository class, which all repositories extend from. We limit the boilerplate by using dynamic programming to determine which Eloquent model to call at runtime.

Implementation

RepositoryInterface

In the repository interface, we define all the standard Eloquent methods. The method signatures here should match the signatures of the corresponding methods in “Illuminate\Database\Eloquent\Model” exactly.

Note: some of the Eloquent methods have been removed for simplicity. To add them back in, simply use the same pattern described below.

UserRepositoryInterface

We define any methods in here that are specific to the User version of the repository. Notice that we extend from RepositoryInterface, so any class that implements this interface must also provide implementations to the methods in RepositoryInterface.

UserRepository

The concrete implementation of the UserRepository. We extend from AbstractRepository to get the implementations for the RepositoryInterface methods, and then implement any methods defined in UserRepositoryInterface in here.

Notice the $modelClassName variable. This is important, because we use it in the AbstractRepository to dynamically call methods on the corresponding Eloquent model.

AbstractRepository

Finally, AbstractRepository performs the dynamic implementation of the different Eloquent methods.

Example Usage

In the example below we can see two usages. The first uses the Eloquent method “find” to retrieve a single record. The second usage uses our custom “findByUsername” method to locate a user.

Conclusion

This approach enables us to gain the benefits of using a repository layer without requiring us to write loads of boilerplate code.

The standard process for adding new repositories is simple.

  • If we only want the standard eloquent functions, we create a blank interface (“SomethingRepositoryInterface”) that extends from “RepositoryInterface”. We then Create the “SomethingRepository” class, implement the “SomethingRepositoryInterface”, extend from “AbstractRepository” and add the $modelClassName  property.
  • If we need to add methods that are specific to the model, we follow the process defined in the previous step, but then add the custom method signatures to “SomethingRepositoryInterface” and the concrete implementations of those methods in “SomethingRepository”
  • Matthew Reschke

    Nice! I have been looking for a great way to do just that. Watching all of laracasts videos about repositories makes me want to use them but they do seem so redundant with eloquent. This is a perfect mix thanks!

    • http://michaelcalkins.com/ Michael Calkins

      I was getting the exact same feeling.

  • http://studioignis.net/ Flatline

    Cool! I do something similar, but to avoid using the model in it’s facade fashion I inject it in the controller consturctor, this way I don’t need to use “call_user_func_array”. Of course this must be done on each repository implementation, in order to be able to do this in the abstract, you could just inject the Laravel’s container as a dependency and use it to get the model like this:


    model = $app[$this->modelClassName];
    }

    // ...
    }

    Now you can just use $this->model->find($id) for example.

    Edit: uhmm, the code just got all mangled, I hope you can see it :S

    • mcsanantonio

      Hi, I used your script and an error was shown:

      What do you think seems to be the problem? Thank you 🙂

      ReflectionException in Container.php line 737:Class Users does not exist

      • Jo

        i got the same error. ReflectionException in Container.php line 738:Class User does not exist

        • Jo

          just a namespace issue. Put AppUser instead of just User in UserRepository.php
          protected $modelClassName = ‘AppUser’;

  • Jhon Wang

    Looks good, but I wonder if we can find alternative to call_user_func_array, maybe using the real object instance, instead of calling the facade ?

    • Jo

      did you try Flatline’s approach? I get an error ReflectionException in Container.php line 738:Class User does not exist. Did you have any success?

  • viniciusmr3

    Great article! I am just about to implement repositories in a large project and your solution seems to fit perfect to the scenario. Many thanks for sharing it! 😉

  • Mike Ritter

    So, where do you define relationships in this workflow?

    • ULTIMATE

      Relationships still go into models.
      Repository layer is not to omit models but to reduce the dependencies access of controller on models.

  • Pete McFarlane

    I’m just learning about the repository pattern, and it seems really useful. I like how it abstracts my models from my data source, and how I can combine multiple data sources through a repository to create a model. But do you generally find you need a repository for each of your models? That seems like a lot of repositories! I appreciate that using an abstract as explained in this article can reduce the boiler plate code in most of those repositories to just a couple of lines, but are the extra class files a necessary extra for this level of abstraction or is there an alternative way? Thanks

    • http://www.coreymcmahon.com/ Corey McMahon

      Hi Pete,

      Thanks for the kind words, glad you found the article useful. In answer to your question, I’d say: it depends.

      On my most recent projects, I’ve actually been taking the approach you mention. I’ll build out a smaller number of more complicated repositories (e.g.: a single repo will touch more than 1 model), so the pattern discussed above isn’t applicable.

      However, on large projects I think the pattern above is still applicable. It works particularly well when you have a service layer which then uses the repository classes, example here:

      https://gist.github.com/coreymcmahon/ffe69cdeb3d9b2f5b759

      Makes it really easy to mock things out for tests, and also nicely separates the ORM from your domain logic code.

      • http://mikeritteroline.com Mike Ritter

        This kind of reiterates Taylor’s point in ‘Apprentice to Artisan’ of dumping the ‘Model’ directory altogether.

        • http://www.coreymcmahon.com/ Corey McMahon

          Yeah, I think that’s a good approach. I always move my models inside the namespace of the project, eg: AcmeModels*

  • Ekambaram

    Hi used your pattern to write my laravel repositories but i am able to create a resource but unable retrieve all resources. My full implementation is here http://stackoverflow.com/questions/26648618/repository-design-pattern-in-laravel4-1-retriving-all-data … what am i doing wrong?

  • Kelly Te Huna

    It appears all your code samples have disappeared :/ Any chance of reviving those?

    • http://www.coreymcmahon.com/ Corey McMahon

      Hi Kelly,

      Code samples are working for me. They’re hosted on Gist. Are you sure it’s not a browser / connectivity problem on your end?

  • mcsanantonio

    Hi Corey! 🙂 thank you for this awesome article. However, when I tried applying it to my controller an error shows up that the repository is not instantiable. I am just a beginner and don’t have any ideas on Laravel’s IoC. Thank you

    • Suhaib Tamimi

      you have to create service provider

  • Jo

    Hi Corey, I have tried to test your implementation using a UserManager.php service. I get the following error
    ErrorException in Repository.php line 35:call_user_func_array() expects parameter 1 to be a valid callback, class ‘User’ not found
    This occurs whether I call find or create methods.

    Do you have any idea where I’ve gone wrong?

  • Dave Stewart

    I know this is an old post, but I recently built the same thing for a resources service layer between controllers and models.

    However, rather than a bunch of repeated call_user_func_array()s, I have a query() method:

    /**
    * Query calls on arbitrary models
    *
    * @param $method string The method to call
    * @param $parameters,... mixed unlimited OPTIONAL number of additional variables to display
    * @return Collection|Eloquent
    */
    protected function query($method, $parameters = null)
    {
    $callable = $this->class . '::' . $method;
    if(func_num_args() > 1)
    {
    return call_user_func_array($callable, array_slice(func_get_args(), 1));
    }
    return call_user_func($callable);
    }