In the previous Laravel repository pattern tutorial, I gave an example of a service repository pattern. Now in this tutorial, we will build a complete crud application in Laravel 9 using a service repository pattern. For this repository pattern Laravel crud, we have to go a long way. No problem, we will see step by step.

I assume, you already know the Laravel repository pattern tutorial. if you do not know about it, no problem. I am here to give a complete solution for Laravel 9 repository pattern crud.

In this tutorial, I will create a 'user' crud system using a repository design pattern. There will be three fields, name, email, and password. So do not waste your time, let's start Laravel repository pattern crud:

See the Laravel 9 repository pattern crud tutorial preview image below:

User List:

user-list-with-repository-pattern

User Create Page:

create-new-user-repository-pattern-laravel

Show Details Page:

user-show-repository-pattern-crud-laravelUser Edit Page:

user-edit-repository-pattern

Step 1: Download Fresh Laravel

In this first step, we need a fresh Laravel 9 application for the Laravel 9 repository pattern crud tutorial. So download it by the below command:

composer create-project laravel/laravel example-app

 

Step 2: Create Migration

We are going to use the User model. So no need to create a model and migration. 

Now look at the default migration like below:

database/migrations

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
};

 

And the model:

app/Models/User.php

<?php

namespace App\Models;

use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];
}

 

Step 3: Connect database

After successfully installing the Laravel app and then configuring the database setup. We will open the ".env" file and change the database name, username, and password in the env file to create a Laravel 9 repository pattern crud.

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=Enter_Your_Database_Name
DB_USERNAME=Enter_Your_Database_Username
DB_PASSWORD=Enter_Your_Database_Password

 

Now run php artisan migrate command to update the database. 

php artisan migrate

 

Read also: Why And When To Use Interface In Laravel Application

 

Step 4:  Create Route

Now in this, create a resource route like the one below to complete the crud application in Laravel. I am going to use a resource controller. So define the resource route like the below:

routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;

Route::resource('user',UserController::class);

 

Now run php artisan route:list command to check the available route which we are going to use for user crud with ima. After running the command you will see the below output.

laravel-user-crud-repository-pattern

Read also:  Laravel 9 CRUD Tutorial Using Query Builder

 

Step 5: Create Interface

In this step, we have to create an interface cause we are going to create a Laravel repository pattern system. So create and update it like the below:

App\Repository\IUserRepository.php

<?php

namespace App\Repository;

use App\Models\User;
use Illuminate\Pagination\LengthAwarePaginator;

interface IUserRepository 
{
    public function list() : LengthAwarePaginator;
    public function findById($id) : User;
    public function storeOrUpdate( $id = null, $collection = [] );
    public function destroyById($object);
}

 

Look at that, we defined four methods inside the interface and all of the methods do different tasks. The 'list()' method will return all the users and the 'findById()' method will return only a single user by id and the 'storeOrUpdate()' method will store or update the users and the 'destroyById()' method will destroy the single user by id.

 

Step 6: Create Service Class

Now time to create a 'UserRepository' class that implements the user interface and completes the task from here. So create this class and update it like the below:

App\Repository\UserRepository.php

<?php

namespace App\Repository;

use App\Models\User;
use App\Repository\IUserRepository;
use Illuminate\Support\Facades\Hash;
use Illuminate\Pagination\LengthAwarePaginator;

class UserRepository implements IUserRepository
{   
    protected $user = null;

    public function list() : LengthAwarePaginator 
    {   
        return User::paginate(10);
    }

    public function findById($id) : User
    {
        return User::find($id);
    }

    public function storeOrUpdate($id = null, $data = [] )
    {   
        if(is_null($id)) {
            $user = new User;
            $user->name = $data['name'];
            $user->email = $data['email'];
            $user->password = Hash::make('password');
            $user->save();

            return $user;
        }

        $user = User::find($id);
        $user->name = $data['name'];
        $user->email = $data['email'];
        $user->password = Hash::make('password');
        $user->save();

        return $user;
    }
    
    public function destroyById($id)
    {
        return User::find($id)->delete();
    }
}

 

Step 7: Create Controller

Now in this step, we have to create a UserController to define this method to create a repository pattern Laravel crud.

php artisan make:controller UserController

 

Now update the controller like the below:

app/Http/Controllers/UserController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Repository\IUserRepository;

class UserController extends Controller
{   
    protected $user;

    public function __construct(IUserRepository $user)
    {
        $this->user = $user;
    }

    public function index()
    {   
        $users = $this->user->list();

        return view('user.index',compact('users'));
    }

    public function create()
    {
        return view('user.create');
    }

    public function store(Request $request)
    {   
        $data = $request->validate([
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6',
        ]);

        $this->user->storeOrUpdate($id = null, $data);

        return redirect()->route('user.index')->with([
            'message' => 'User added successfully!', 
            'status' => 'success'
        ]);
    }

    public function show($id)
    {   
        $user = $this->user->findById($id);

        return view('user.show',compact('user'));
    }

    public function edit($id)
    {   
        $user = $this->user->findById($id);

        return view('user.edit',compact('user'));
    }

    public function update(Request $request, $id)
    {   
        $data = $request->validate([
            'name' => 'required',
            'email' => 'required'
        ]);

        $this->user->storeOrUpdate($id, $data);

        return redirect()->route('user.index')->with([
            'message' => 'User updated successfully!', 
            'status' => 'success'
        ]);
    }

    public function destroy($id)
    {   
        $this->user->destroyById($id);

        return redirect()->route('user.index')->with([
            'message' => 'User deleted successfully!', 
            'status' => 'success'
        ]);
    }
}

 

Step 8: Binding Service Class With Interface

Look at that, our interface, service repository, and controller are ready to go. Now we have to do one more thing. We have to bind the repository class to our interface before using the interface. If we do not bind then you will face an error like that:

 

Target [App\Repository\IUserRepository] is not instantiable while building [App\Http\Controllers\UserController].

 

So now bind it in your 'AppServiceProvider' that way:

App\Providers\AppServiceProvider.php

<?php

namespace App\Providers;

use App\Repository\UserRepository;
use App\Repository\IUserRepository;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{

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

    public function boot()
    {
        
    }
}

 

Step 9: Create Views

We are almost there. Just we have to create five views files before completing Laravel repository pattern tutorial with crud.

  • index.balde.php
  • create.blade.php
  • edit.blade.php
  • show.blade.php
  • app.blade.php

So create these files inside the following path and update them like below:

resources/views/user/index.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card p-2 mb-2" style="font-weight: 700">Repository Design Pattern in Laravel 9 With User CRUD - Laravelia</div>
            <div class="card">
                <div class="card-header" style="background: gray; color:#f1f7fa; font-weight:bold;">
                    User List
                    <a href="{{ route('user.create') }}" class="btn btn-success btn-xs py-0 float-end">+ Create New</a>
                </div>
                @if(session('message'))
                <div class="alert alert-{{ session('status') }} alert-dismissible fade show" role="alert">
                    <strong>{{ session('message') }}</strong>
                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                </div>
                @endif
                 <div class="card-body">                    
                    <table class="table-responsive" style="width: 100%">
                        <thead>
                            <th>#</th>
                            <th>Name</th>
                            <th>Email</th>
                            <th>Action</th>
                        </thead>
                        <tbody>
                            @foreach($users as $user)
                            <tr>
                                <td>{{ $loop->index + 1 }}</td>
                                <td>{{ $user->name }}</td>
                                <td>{{ $user->email }}</td>
                                {{-- <td>
                                    @if($user->image)
                                    <img src="{{ asset('storage/images/'.$user->image) }}" style="height: 50px;width:100px;">
                                    @else 
                                    <span>No image found!</span>
                                    @endif
                                </td> --}}
                                <td><a href="{{ route('user.edit',$user->id) }}" class="btn btn-success btn-xs py-0">Edit</a></td>
                                <td><a href="{{ route('user.show',$user->id) }}" class="btn btn-secondary btn-xs py-0">Show</a></td>
                                <td>
                                    <form method="POST" action="{{ route('user.destroy',$user->id) }}">
                                        @csrf
                                        @method('delete')
                                        <button type="submit" class="btn btn-danger btn-xs py-0 text-white">Delete</button>
                                    </form>
                                </td>
                            </tr>
                            @endforeach
                        </tbody>
                    </table>
                    <center class="mt-5">
                        {{  $users->links() }}
                    </center>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

 

resources/views/user/edit.blade.php

@extends('layouts.app')

@push('style')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header" style="background: gray; color:#f1f7fa; font-weight:bold;">
                    Update User
                    <a href="{{ route('user.index') }}" class="btn btn-success btn-xs py-0 float-end">Back</a>
                </div>
                @if($errors->any())
                    {!! implode('', $errors->all('<div>:message</div>')) !!}
                @endif
                 <div class="card-body">                    
                    <form class="w-px-500 p-3 p-md-3" action="{{ route('user.update',$user->id) }}" method="post" enctype="multipart/form-data">
                        @csrf
                        @method('patch')
                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label">Name</label>
                            <div class="col-sm-9">
                                <input type="text" class="form-control" name="name" placeholder="Name" @error('name') is-invalid @enderror value="{{ $user->name }}">
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                
                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label">Email</label>
                            <div class="col-sm-9">
                                <input type="email" class="form-control" name="email" placeholder="Email" @error('email') is-invalid @enderror value="{{ $user->email }}">
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label"></label>
                            <div class="col-sm-9">
                                <button type="submit" class="btn btn-secondary btn-block text-danger">Submit</button>
                            </div>
                        </div>
                      </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

 

resources/views/user/show.blade.php

@extends('layouts.app')

@push('style')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header" style="background: gray; color:#f1f7fa; font-weight:bold;">
                    User details
                    <a href="{{ route('user.index') }}" class="btn btn-success btn-xs py-0 float-end">Back</a>
                </div>
                 <div class="card-body">                    
                    <div class="row mb-3">
                        <label class="col-sm-3 col-form-label">Name</label>
                        <div class="col-sm-9">
                            <input type="text" class="form-control" name="name" value="{{ $user->name }}">
                        </div>
                    </div>
            
                    <div class="row mb-3">
                        <label class="col-sm-3 col-form-label">Email</label>
                        <div class="col-sm-9">
                            <input type="email" class="form-control" name="email" value="{{ $user->email }}">
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

 

resources/views/user/create.blade.php

@extends('layouts.app')

@push('style')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header" style="background: gray; color:#f1f7fa; font-weight:bold;">
                    Create New User
                    <a href="{{ route('user.index') }}" class="btn btn-success btn-xs py-0 float-end">Back</a>
                </div>
                @if($errors->any())
                    {!! implode('', $errors->all('<div>:message</div>')) !!}
                @endif
                 <div class="card-body">                    
                    <form class="w-px-500 p-3 p-md-3" action="{{ route('user.store') }}" method="post" enctype="multipart/form-data">
                        @csrf
                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label">Name</label>
                            <div class="col-sm-9">
                                <input type="text" class="form-control" name="name" placeholder="Name" @error('name') is-invalid @enderror>
                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>
                
                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label">Email</label>
                            <div class="col-sm-9">
                                <input type="email" class="form-control" name="email" placeholder="Email" @error('email') is-invalid @enderror>
                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label">Password</label>
                            <div class="col-sm-9">
                                <input type="password" class="form-control" name="password" placeholder="Password" @error('password') is-invalid @enderror>
                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <label class="col-sm-3 col-form-label"></label>
                            <div class="col-sm-9">
                                <button type="submit" class="btn btn-success btn-block text-white">Submit</button>
                            </div>
                        </div>
                      </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

 

Now we need to create a master file. So let's create a app.blade.php file and add it to the following path:

resources/views/layouts/app.blade.php

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>
    
    <!-- Scripts -->
    @vite(['resources/sass/app.scss', 'resources/js/app.js'])
    @stack('style')
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    Laravelia
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Left Side Of Navbar -->
                    <ul class="navbar-nav me-auto">

                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav ms-auto">
                        <!-- Authentication Links -->
                        @guest
                            @if (Route::has('login'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                            @endif

                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <li class="nav-item dropdown">
                                <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                    {{ Auth::user()->name }}
                                </a>

                                <div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdown">
                                    <a class="dropdown-item" href="{{ route('logout') }}"
                                       onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                        {{ __('Logout') }}
                                    </a>

                                    <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                        @csrf
                                    </form>
                                </div>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="py-4" style="background: #f1f7fa;">
            @yield('content')
        </main>
    </div>
    @stack('script')
</body>
</html>

 

Now all are set to go. Now run php artisan serve command to start the development server and visit the following URL to check this Laravel 9 repository pattern crud tutorial.

URL
http://localhost:8000/user

 

Recommended: What Is Service Container And When To Use It In Laravel

 

Conclusion

Now we are in conclusion. Is this ok? Now we know the repository pattern Laravel crud and we can implement the Laravel repository pattern system. So hope this Laravel repository pattern crud tutorial will help you.