Use Repository Pattern with Laravel
Tags: laravel, programming, tutorial, php
Published: 2022-12-11 21:46:06

> A repository can be defined as a layer of abstraction between the domain and data mapping layers, one that provides an avenue of mediation between both, via a collection-like interface for accessing domain objects.

Modern PHP frameworks, such as Laravel and Symfony, interact with databases via Object-relational mappers (ORMs); Symfony uses Doctrine as its default ORM and Laravel uses Eloquent.
Both take different approaches in how database interaction works. With Eloquent, Models are generated for each database table, forming the basis of interaction. Doctrine, however, uses the Repository pattern where each Entity has a corresponding repository containing helper functions to interact with the database. While Laravel doesn't provide this functionality out of the box, it is possible to use the Repository pattern in Laravel projects.

A key benefit of the Repository pattern is that it allows us to use the Principle of Dependency Inversion (or code to abstractions, not concretions). This makes our code more robust to changes, such as if a decision was made later on to switch to a data source that isn't supported by Eloquent.

It also helps with keeping the code organized and avoiding duplication, as database-related logic is kept in one place. While this benefit is not immediately apparent in small projects, it becomes more observable in large-scale projects which have to be maintained for many years.

In this article, I will show you how to implement the Repository pattern in your Laravel applications. To do that, we will build an API to manage orders received from clients for a company.

## Getting started

Create a new Laravel project and cd into the directory using the following commands

```php
laravel new note_block
cd note_block
```

## Set up the database

For this tutorial, we'll use MySQL as our database. To do that, in the .env file, update the database-related parameters as shown below.

Finally, using your preferred database management application, create a new database called note_block.

## Generate the initial data for the database

We are building an order management application, so let's create the model for it by running the following command.

```php
php artisan make:model User -a
```

The `-a` argument lets Artisan know that we want to create a migration file, seeder, factory, and controller for the User model.

The command above will create five new files:

```
- controller in app/Http/Controllers/UserController.php

- database factory in database/factories/UserFactory.php

- migration file in database/migrations/YYYY_MM_DD_HHMMSS_create_users_table.php

- model located in app/Models/User.php

- seeder file in database/seeders/UserSeeder.php
```
In `database/migrations/YYYY_MM_DD_HHMMSS_create_users_table.php`, update the up function to match the following.

```php
public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->text('username');
        $table->string('email');
        $table->string('password');
        $table->boolean('status')->default(false);
        $table->timestamps();
    });
 }
```
As specified in the migration file, the order table will have the following columns:

Next, let's update UserFactory so that it can generate a dummy users to seed the database with. In `database/factories/UserFactory.php`, update the definition function to match the information in your migration file.

Next, open `database/seeders/UserSeeder.php` and update the `run` function to match the following.

```php
public function run() 
{
    User::factory()->times(50)->create();
}
```
In `database/seeders/DatabaseSeeder.php`, add the following to the run function.

```php
$this->call([UserSeeder::class]); 
```

Finally, run your migrations and seed the database using the following command.

```php
php artisan migrate --seed
```

## Create the Repository

Before we create a repository for the Order model, let's define an interface to specify all the methods which the repository must declare. Instead of relying directly on the repository class, our controller (and any user component we may build in the future) will depend on the interface.

This makes our code flexible because, should it become necessary to make a change in the future, the controller remains unaffected. For instance, if we decided to outsource user management to a 3rd party application, we can build a new module that conforms to UserRepositoryInterface's signature and swap the binding declarations and our controller will work exactly as expected - without touching a single line of code in the controller.

In the app directory, create a new folder called `Contract`. Then, in this folder, create a new file called `UserRepositoryInterface.php` and add the following code to it.

```php
<?php

namespace App\Contract;

use App\Models\User;

interface UserRepositoryInterface 
{
    public function getAllUsers();
    public function getUserById(User $user);
    public function deleteUser(User $user);
    public function createUser(array $attributes);
    public function updateUser(User $user, array $attributes);
}

```
After, in the app folder, create a new folder called `Repositories`. In this folder, create a new file called `UserRepository.php` and add the following code to it.

```php
<?php

namespace App\Repositories;

use App\Contract\UserRepositoryInterface;
use App\Models\User;

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

    public function getUserById(User $user) 
    {
        return $user;
    }

    public function deleteUser(User $user) 
    {
        $user->delete();
    }

    public function createUser(array $attributes) 
    {
        return User::create($attributes);
    }

    public function updateUser(User $user, array $attributes) 
    {
        return $user->update($attributes);
    }
}
```
Apart from the flexibility provided by the interface, encapsulating queries in this manner has the added advantage that we don't have to duplicate queries throughout the application.

## Creating the controllers

With our repository in place, let's add some code to our controller. Open `app/Http/Controllers/UsersController.php` and update the code to match the following.

```php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use App\Contract\UserRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Hash;

class UsersController extends Controller 
{
    public function __construct(
       private UserRepositoryInterface $repository
    ) 
    {
    }

    public function index(): JsonResponse 
    {
        return response()->json([
            'data' => $this->repository->getAllUsers()
        ]);
    }

    public function store(Request $request): JsonResponse 
    {
        $attributes = $request->only([
            'username',
            'email'
        ]);


        return response()->json(
            [
                'data' =>  $this->repository->createUser(array_merge($attributes, [
   'password' => Hash:make($request->input('password')

]))
            ],
            Response::HTTP_CREATED
        );
    }

    public function show(User $user): JsonResponse 
    {
       
        return response()->json([
            'data' => $user
        ]);
    }

    public function update(User $user, Request $request): JsonResponse 
    {
        $attributes = $request->only([
            'username',
            'email'
        ]);

        return response()->json([
            'data' => $this->repository->updateUser($user, $attributes)
        ]);
    }

    public function destroy(User $user): JsonResponse 
    {
        
        $this->repository->deleteUser($user);

        return response()->json(null, Response::HTTP_NO_CONTENT);
    }
}
```


## Bind the interface and the implementation

The last thing we need to do is bind UserRepository to UserRepositoryInterface in Laravel's Service Container; we do this via a Service Provider. Create one using the following command.

```php
php artisan make:provider RepositoryServiceProvider
```

Open _`app/Providers/RepositoryServiceProvider.php`_ and update the register function to match the following.

```php
public function register() 
{
    $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
 }
```

> Remember to include the import statement for UserRepository and UserRepositoryInterface.

Finally, add the new Service Provider to the providers array in `config/app.php`.

```php
'providers' => [
    // ...other declared providers
    App\Providers\RepositoryServiceProvider::class,
];
```

## Adding the routes

To map each method defined in the controller to specific routes, add the following code to `routes/api.php`.

```php
Route::get('users', [UsersController::class, 'index']);
Route::get('users/{user}', [UsersController::class, 'show']);
Route::post('users', [UsersController::class, 'store']);
Route::put('users/{user}', [UsersController::class, 'update']);
Route::delete('users/{user}', [UsersController::class, 'delete']);
```

## Test the application

Run the application using the following command.

```php
php artisan serve
```
By default, the served application will be available at [localhost:8000](http://127.0.0.1:8000/). Using Postman or cURL, we can make requests to our newly created API.

Run the following command to test the `/api/users` endpoint using cURL:

### That's how to use the Repository Pattern in a Laravel application

In this article, we learned about the Repository pattern and how to use it in a Laravel application. We've also seen some of the benefits it offers a large-scale project - one of which is loosely coupled code where we are coding to abstractions, not concrete implementations.