DSC00409
1 Oct 2019 16 min to read

Laravel Repository Pattern – How to use & why it matters

The first thing we need to know to implement a repository pattern in the Laravel application is to understand what Repository Pattern is and what are the benefits of using it.

In this article you’ll learn about:

  • Main benefits of using Repository Pattern 
  • How to implement a Repository Pattern in Laravel?
  • We will show you step by step how to start code and implement it in Laravel app

What’s important

A repository is a separation between a domain and a persistent layer. The repository provides a collection interface to access data stored in a database, file system or external service. Data is returned in the form of objects.

The main idea to use Repository Pattern in a Laravel application is to create a bridge between models and controllers. In other words, to decouple the hard dependencies of models from the controllers. The model should not be responsible for communicating with or extracting data from the database. A model should be an object that represents a given table/document/object or any other type in our data structure and this should be its sole responsibility. Therefore, to keep your Laravel code clean and safe, it is worth using repositories to separate the responsibility for which the model should never be responsible.

The use of Repository Pattern has many benefits, below is a list of the most important ones:

  • Centralization of the data access logic makes code easier to maintain
  • Business and data access logic can be tested separately
  • Reduces duplication of code
  • A lower chance for making programming errors

In most Laravel applications you may encounter such a code.

class UsersController extends Controller
{
   public function index()
   {
       $users = User::all();

       return view('users.index', [
           'users' => $users
       ]);
   }
} 

At first glance, it doesn’t look that bad. However, it is worth to think what if the client proposes to change the data structure and instead of in MySQL/Postgresql from now on we are going to keep the data somewhere else, in the data engine which is not supported by Eloquent? When we write such a code, such a change may turn out to be very difficult to implement, or even impossible! That’s why it’s very dangerous to write code this way, every implementation should be based on interfaces, so in case of changes you don’t need to change the code in the whole application, but only create another class implementing the interface. This should be the code above written in a correct way. (The code above should be written in a correct way).


class UsersController extends Controller
{
   private $userRepository;
  
   public function __construct(UserRepositoryInterface $userRepository)
   {
       $this->userRepository = $userRepository;
   }

   public function index()
   {
       $users = $this->userRepository->all();

       return view('users.index', [
           'users' => $users
       ]);
   }
}

In this particular example, when a client wishes to change the data structure, it is child’s play to implement these changes into our application! We simply create a class that implements UserRepositoryInterface and contains the logic that will allow us to pull it out in a new way and everything works again. That is why it is so important to write code in such a way that even when the client comes with the most difficult change we will be able to deal with it easily. The repository template greatly facilitates this process!

 

Let’s start with code and implement it in your Laravel app!

Now you know the benefits of repository pattern in Laravel, so let’s implement it in your application! In the beginning, we will start with a folders structure. We should start by creating a Repository folder into our app folder. Inside a newly created folder, we should create another folder with a current repositories implementation, we use Eloquent this time, so let’s name it Eloquent. So the final file structure should look like this:


-- app
---- Repository
------ Eloquent
-------- UserRepository.php
-------- BaseRepository.php
------ UserRepositoryInterface.php
------ EloquentRepositoryInterface.php

One thing that might make you wonder at this stage – what exactly is the BaseRepository and EloquentRepositoryInterface? Let me explain this quickly. It will be just a parent class that every eloquent repository implementation class will be extended. This class will store the methods that are common use in every repository. So the BaseRepository class will look like this:

EloquentRepositoryInterface.php



namespace App\Repository;


use Illuminate\Database\Eloquent\Model;

/**
* Interface EloquentRepositoryInterface
* @package App\Repositories
*/
interface EloquentRepositoryInterface
{
   /**
    * @param array $attributes
    * @return Model
    */
   public function create(array $attributes): Model;

   /**
    * @param $id
    * @return Model
    */
   public function find($id): ?Model;
}

BaseRepository.php


<?php   

namespace App\Repository\Eloquent;   

use App\Repository\EloquentRepositoryInterface; 
use Illuminate\Database\Eloquent\Model;   

class BaseRepository implements EloquentRepositoryInterface 
{     
    /**      
     * @var Model      
     */     
     protected $model;       

    /**      
     * BaseRepository constructor.      
     *      
     * @param Model $model      
     */     
    public function __construct(Model $model)     
    {         
        $this->model = $model;
    }
 
    /**
    * @param array $attributes
    *
    * @return Model
    */
    public function create(array $attributes): Model
    {
        return $this->model->create($attributes);
    }
 
    /**
    * @param $id
    * @return Model
    */
    public function find($id): ?Model
    {
        return $this->model->find($id);
    }
}

So that’s our base repository. We inject there model class into constructor and store methods that may be used in every eloquent repository in this current example it will be just create() and find() methods.

We got the parent class so the next step is to implement our user repository!

UserRepositoryInterface.php


<?php
namespace App\Repository;

use App\Model\User;
use Illuminate\Support\Collection;

interface UserRepositoryInterface
{
   public function all(): Collection;
}

UserRepository.php


<?php

namespace App\Repository\Eloquent;

use App\Model\User;
use App\Repository\UserRepositoryInterface;
use Illuminate\Support\Collection;

class UserRepository extends BaseRepository implements UserRepositoryInterface
{

   /**
    * UserRepository constructor.
    *
    * @param User $model
    */
   public function __construct(User $model)
   {
       parent::__construct($model);
   }

   /**
    * @return Collection
    */
   public function all(): Collection
   {
       return $this->model->all();    
   }
}

And that’s it! You have just implemented the repository pattern in laravel. Let’s use it in our application then!

To let our application know which implementation of which interface we want to use we need to create Laravel service provider. We will name it RepositoryServiceProvider, so let’s type (it) in our console method:


php artisan make:provider RepositoryServiceProvider

After creating this file let’s open it and add some new line of codes:


<?php 

namespace App\Providers; 

use App\Repository\EloquentRepositoryInterface; 
use App\Repository\UserRepositoryInterface; 
use App\Repository\Eloquent\UserRepository; 
use App\Repository\Eloquent\BaseRepository; 
use Illuminate\Support\ServiceProvider; 

/** 
* Class RepositoryServiceProvider 
* @package App\Providers 
*/ 
class RepositoryServiceProvider extends ServiceProvider 
{ 
   /** 
    * Register services. 
    * 
    * @return void  
    */ 
   public function register() 
   { 
       $this->app->bind(EloquentRepositoryInterface::class, BaseRepository::class);
       $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
   }
}

The last step is to register this service provider in our config/app.php. Open this file and add to providers our provider  App\Providers\RepositoryServiceProvider::class

Now our application knows what class it should use when we type an objects by its interfaces. So we can simply use makinglogic like this in controller:


class UsersController extends Controller
{
   private $userRepository;
  
   public function __construct(UserRepositoryInterface $userRepository)
   {
       $this->userRepository = $userRepository;
   }

   public function index()
   {
       $users = $this->userRepository->all();

       return view('users.index', [
           'users' => $users
       ]);
   }
}

Our app knows that we are looking for App\Repository\Eloquent\UserRepository implementation. In case we want to use other implementation than eloquent we simply change App\Repository\Eloquent\UserRepository in our service provider to e.g App\Repository\Mongo\UserRepository. Our application will work the same way as before even if the data engine has changed and we didn’t change even 1 line of code in our controller or anywhere else! 

That’s it! You have implemented a repository pattern in your app. Now you can add new repositories for the other models you are using in your application.

Finally, a brief summary of what is worth noting when implementing a repository pattern.

  • Each repository should have its own interface implemented! Never create a repository without implementing its own interface and do not create one interface for all repositories. It is not a good practice!
  • You should always inject your repository using Dependency Injection instead of making an instance using the new keyword. As a type always use interface, not an implementation! If you inject objects into the class it is easier to write unit tests, you are warning SRP(Single Responsibility Principle) and the code looks more cleaner.
  • Make your code reusable, if any method is used by more repositories than the one you should implement this method into the BaseRepository class to avoid the DRY principle.
  • In your repository inject model in a constructor, don’t use a class as a static. By doing it you can easily mock your model in your unit tests!
Call to action
Do you think that you know how to implement Repository Pattern in Laravel already? If you need any help don’t hesitate to contact us!

Share

SUBSCRIBE our NEWSLETTER

Are you interested in news from the world of software development? Subscribe to our newsletter and receive a list of the most interesting information.

COMMENTS (17) comments arrow

  1. What about adding magic __call function which will handle find, paginate, where and etc..

    /**
    * @param $name
    * @param $arguments
    * @return mixed
    */
    public function __call($name, $arguments)
    {
    return $this->model->{$name}($arguments);
    }

    • I am not a big fan of magic methods, so I rather to avoid using them to be honest. It’s not a good idea to use a magic methods because:

      1. System is harder to understand, especially for newcomers
      2. There is no auto-completion(and this is a major problem actually), and type management by the IDE for refactoring and code-browsing
      3. The documentation (phpdoc) doesn’t match how your code is supposed to be used, and looking at your class doesn’t bring much answers as well. This is confusing.
      4. It’s harder to write unit tests, mock magic methods.
      5. It’s harder to debug code.

      So it produces more problems than it gives a value. For your own sake you should avoid using magic methods too. 🙂

    • If you use the magic __call method, this should be

      public function __call ($method, $args)
      {
      return call_user_func_array ([$this-> model, $method], $args);
      }

      But indeed, as Adrian mentioned, it quickly turns out that the solution is becoming onerous. In my solutions I usually duplicated the methods from the model in the repository

  2. Thanks for this. While it helped to understand the basics of the repository pattern, my feedback on this post would be to give a complete example including the code for the UserRepository in order to understand how to actually bind an eloquent model to a repository. Am I supposed to call parent::__construct(new User); in the UserRepository?

    • The post has been updated as we noticed this part was missing somehow. Please read it again and if it still be confused for you please let us know! 🙂

  3. Agree with @Tim, it’s not enough clear why we have this hierarchy for UserRepository:

    BaseRepository < EloquentRepositoryInterface
    UserRepository < UserRepositoryInterface
    UserRepository < BaseRepository

    I assume UserRepository this is because it has user-specific logic in it and at the same time it wants to benefit from generic repository methods implemented by BaseRepository?

  4. … but the example given has all() method which should be in BaseRepository not in the UserRepositoryInterface since it’s a very generic way to get all Eloquent models, isn’t it?

    • Yes, you’re right. In a real project this should be in BaseRepository, because more than 1 class would probably use it. However, for the purpose of the article I implemented this method in UserRepository in order to make the article easier to understand and not to complicate the matter with a more complicated example. 🙂

  5. hi,thanks for shared your knowledge .i have a big question. after all steps we use eloquent model in controller methods that’s means we don’t good decouple . what do you think?

  6. Agree with @mohammad, the model should be decoupled so as not to depend on Eloquent model in our controllers.

  7. Medicaltipes

    However, you are not even achieving the benefits you preached about with your interface. The Eloquent jargon in your interface and the type hint to return an Eloquent model are coupling it to Eloquent: you gain nothing of what you aimed for in this case. If you were to eventually replace Eloquent, you’ll have a hard time doing so.

    • Yes, you’re absolutely right. This example does not achieve all the benefits I mentioned, because the main purpose of this article was to show how to implement repository pattern in easy way and with a quality that will be “high enough” for most laravel projects. Yes, as you noticed in this particular implementation changing eloquent to something else will be painful, and that’s true, because our repositories refer to Eloquent classes to remedy this we should create appropriate interfaces for our models/collections and change the type hints to those interfaces. Maybe in future I will update this article or create a new one that will show “higher level” implementation that will achive all the benefits I mentioned. 🙂

      • Oluwadamiloju Yusuf

        Isn’t what is needed just to change the type hints in the UserRepositoryInterface to refer to an Interface for collections?? Or can you explain better?

  8. I really enjoyed your implementation, so I’ve copied some parts. I agree with people said return model and collection is not a good way, so I changed it.
    I also changed the directories organization. I think this is more organized and easier to understand.
    |-Repositories
    |—Eloquent
    |—‘–Base
    |——‘–BaseRepository.php
    |——‘–EloquentRepositoryInterface.php
    |—‘–UserRepository.php
    |—UserRepositoryInterface.php

  9. Hi guys, I have found you after I’ve been looking for a solution for weeks now for an api problem. The problem is that I work with multiple tables that are joined together. I’ve extracted some of the logic to a resource file based on one tutorial, then it didn’t look as I expected, so I kept looking and found repository, and I’m full confused. What’s the difference between repos and resources, and how to modify data that comes from several models into a nicely formatted json to send to the controller? An easy example: I have account groups, that has many accounts, that has many transactions. Ho would you return a json of the accounts grouped by account groups, with all its accounts, and their transactions, and for example alter the transaction date format to return date only of mysql timestamp? This is what I try to accomplish. Thank you.

  10. hi, why i am having this error?

    Illuminate\Contracts\Container\BindingResolutionException: Target class [Repositories\UserRepositoryInterface] does not exist. in file C:\xampp\htdocs\blog-api\vendor\laravel\framework\src\Illuminate\Container\Container.php on line 807

  11. You probably didn’t injected you service provider, check if you add the provider into your app.php file

Leave a Reply to ed5 Cancel reply

RECOMMENDED posts