fbpx
repository pattern laravel
Adrian Affek Updated: 10 Sep 2022 6 min to read

Laravel Repository Pattern – PHP Design Pattern

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

In this article, you’ll learn about:

  • Main benefits of using Repository Pattern 
  • How to implement a PHP Repository Pattern in Laravel?
  • We will show you how to start code and implement it in the Laravel-based App.

What’s important – Why do we need a design pattern?

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 of using 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 the PHP 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 of 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 thinking about 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 Eloquent does not support? When we write such a code, such a change may be tough to implement or even impossible! That’s why it’s very dangerous to write code this way. Every implementation should be based on interfaces. 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 correctly. 


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 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 so that even when the client comes with the most difficult change, we can deal with it easily. The repository template greatly facilitates this process!

 

Despite common misconceptions that PHP is a technology of the past, our extensive experience in web software development has proven its ongoing relevance and efficiency. PHP remains a robust choice for many web applications due to its simplicity, extensive community support, and seamless integration with various databases. By deploying PHP in numerous projects, we have enabled startups to launch scalable, secure, and cost-effective platforms quickly. Mike Jackowski COO, ASPER BROTHERS Contact Me

 

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

Now you know the benefits of the repository pattern in Laravel, so let’s implement them in your application! In the beginning, we will start with a folder structure. We should start by creating a Repository folder in our app folder. We should create another folder with a current repository implementation inside a newly created folder. 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 are 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 their model class into the constructor and store methods that may be used in every eloquent repository. In this current example, it will be just created () and find() methods.

We got the parent class, so the next step is implementing 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 a 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 lines 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 objects by its interfaces. So we can use making logic like this in the 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 another implementation other than eloquent, we 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 PHP application.

Finally, a 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!
  • It would help if you always injected your repository using Dependency Injection instead of making an instance using the new keyword. As a type, always use an 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 cleaner.
  • Make your code reusable. If more repositories than one use any method, you should implement this method into the BaseRepository class to avoid the DRY principle.
  • In your repository, inject the 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!
default avatar asper brothers

Adrian Affek

Backend Developer

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 (28) 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?

      • Yes, you’re right. ActiveRecord is not good solution for this kind of problem, doctrine entities are best suited for this but unfortunately its not easy to use doctrine in laravel project

    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.

      • Hey!
        I am 100% sure that noone will explain you how to achieve that because “Repository Pattern” is wrong out of the box.
        Problems you presented cannot be handled with repository pattern without huge amount of additional queries, deadly slowing your app.

        Every f… “repository pattern” article stops here, because example given above is the only example when repository pattern will do the job.
        In any more complicated case it simple does not work.

        I faced this problem so many times and i am just tired of that concept.
        this pattern:
        – complicates development process without befetints that may be wotrh it
        – anforces you to think that you will somehow change your entire database to something absolutely weird (eg: api call. And yes api call may be used, but try to use something like join ^^).

        You may agree or disagree of that, but this came from years of my experience.

        • This is just a pattern, it does not have anything to do with your issue ‘huge amount of additional queries’. Secondly, I disagree with your second statement, ‘enforces you to think that you will somehow change your entire’.

    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

    12. Thanks a lot . This blog helped me so much to understand the Repository pattern . But can you please explain to me that when i want to call a method of BaseRepository like (create Or find) in UserController how i call the method.

    13. after many years working with Laravel, i get the point: using Repository pattern as a proxy for Eloquent model methods is totally bullshit.

    14. Sorry, I did everything according to the article, but the application reports Interface ‘App \ Repositories \ EloquentRepositoryInterface’ not found in ……\app\Repositories\Eloquents\BaseRepository.php:9

    15. Thanks a lot for that precious information.
      Nevertheless, I hardly get the point of why the interface should not be shared between the repositories.

      It’s another topic, but knowing how to create the command `php artisan make:repository UserRepository` would be magic 🙂

      Thanks again

    16. Julian Somoza

      Excellent article (and comments). In my case, I never implement the repository on controllers, as controllers wouldn’t manage any database interaction. Instead, I inject it into my services. Greetings from Argentina!

    17. Dmytro Morozov

      You should not use repository pattern with ActiveRecord – all logic for receiving and updating models already stays within models. All you did is just created a useless layer of abstraction and probably caused N+1 querry issue because you will get data on top of the Eloquent but not within. Eloquent has its own method to avoid repeatable code, and it’s called scopes.

    18. hey there
      I think the BaseRepository you have extended is extra in UserRepository because instead of parent::all() you used it self query
      have fun 🙂

    19. Why users still make use of to read news papers when in this technological world
      everything is accessible on net?

    ADD COMMENT

    Download our Free MVP Prompt Template

    Uncover the ideal PHP technology for your app with our ChatGPT Prompt.

      RELATED articles