When you have large projects then managing the code structure, and files inside the project become a tedious job. Sometimes due to an unorganized way the code goes mesh and complicated. To overcome these kinds of issues we can follow a design pattern. A design pattern is an advanced approach for organizing the code structure. It allows us to use the principle of dependency inversion. It means separating the business logic and keeping it in one place without showing it in the main file. This makes our code more robust, clean, and secure. There are various design patterns available to achieve this in Laravel. In this post, I will gonna talk about the Repository Design Pattern. It is one of the popular design patterns and it is very beneficial for large projects. So, let’s start with the implementation of the Repository pattern in Laravel 9.
We are going to achieve the below results using the Repository Design Pattern in Laravel 9.
Prerequisites
It is not recommended to use Laravel 9 (The latest version of now) for implementing the Repository design pattern. Even you can use it in the previous versions of Laravel as well.
I am going to have a fresh project setup in Laravel 9. In order to do that, you will need to have the following configurations and tools.
- PHP >=8.0.2
- Composer
- Apache/Nginx Server
- VS Code Editor (Optional)
- MySQL (version > 5)
Now, let’s jump to the first step of the project setup.
Create a Laravel Project For Repository Design Pattern Implementation
For creating a project, I will use composer. You can open the terminal and create a project using the below command.
composer create-project --prefer-dist laravel/laravel repository-app
I will be creating a basic CRUD application by applying the Repository Design pattern.
Once the project is created, let’s configure a database for it.
How to Create a Zip File of Any Files and Download in Laravel 9
Create and Configure a Database For Laravel Project
For the database, I will be using MySQL and for managing, I am using phpMyAdmin. Here, I have created a database.
CREATE DATABASE laravel_db;
After creating the database, it’s time to connect it with the project. Hence, navigate to the .env file of the project and configure the DB.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_db
DB_USERNAME={{ DB_USERNAME }}
DB_PASSWORD={{ DB_PASSWORD }}
Now, the time is to create a CRUD application using the Repository pattern.
Create a Model, Migration, and Controller
I will be creating a simple blog application in which we will have the post content. So, we will be applying CRUD operations for the blog post using the Repository Design Pattern.
Therefore, hit the below command to generate a model, migration, and controller.
php artisan make:model Post -mc
The command will generate these three files together.
Now, in the next step, you need to create a table schema for the posts table. Let’s do this as shown below.
<?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('posts', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable();
$table->text('content')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
};
Similarly, you need to add a fillable property in the corresponding model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'content'
];
}
Now, the table schema is ready to be migrated to the database. Let’s migrate the table.
For migrating the table(s) you need to hit the below artisan command.
php artisan migrate
In the next step, let’s do a setup for the Repository Design Pattern for this project.
How to Use Group Routes For the Same Controller in Laravel 9
Repository Design Pattern Setup in Laravel
At the very first step, you need to create a folder named Interfaces inside the app folder. Now, inside this folder create a new interface file named PostInterface.php.
So, the folder structure will be like this –
app
|___ Interfaces
| |______ PostInterface.php
|
|
After that, you need to declare the possible functions as required. Here, we are going to create a CRUD application. Hence, we will be declaring the below functions.
<?php
namespace App\Interfaces;
interface PostInterface
{
public function getAllPosts();
public function createPost($request);
public function getPostById($postId);
public function updatePost($request, $postId);
public function deletePost($postId);
}
After defining the functions in the Interface, we will create the Repository where this interface will be implemented.
How to Clear Application Cache without Command Line in Laravel
Create a Repository in Laravel
Similar to the Interfaces, create another folder named Repositories inside the app folder. Now, inside this folder, create a class named PostRepository.php. So, the folder structure will be looking as shown below.
app
|___ Interfaces
| |______ PostInterface.php
|
|___ Repositories
| |______ PostRepository.php
|
|
Once, you are done with PostRepository class, let’s add the implementation of the interface methods.
<?php
namespace App\Repositories;
use App\Interfaces\PostInterface;
use App\Models\Post;
class PostRepository implements PostInterface
{
/**
* Function : Get All Posts
* @param NA
* @return posts
*/
public function getAllPosts()
{
return Post::all();
}
/**
* Function : Create Post
*
* @param [type] $request
* @return post
*/
public function createPost($request)
{
return Post::create([
'title' => $request->title,
'content' => $request->content,
]);
}
/**
* Function : Get Post By Id
* @param [type] $id
* @return post
*/
public function getPostById($id)
{
return Post::find($id);
}
/**
* Function : Update Post
*
* @param [type] $request
* @param [type] $id
* @return post
*/
public function updatePost($request, $id)
{
$post = Post::find($id);
if ($post) {
$post['title'] = $request->title;
$post['content'] = $request->content;
$post->save();
return $post;
}
}
/**
* Function : Delete Post
* @param [type] $id
* @return void
*/
public function deletePost($id)
{
$post = Post::find($id);
if ($post) {
return $post->delete();
}
}
}
Here, in this repository class, we have implemented the methods of the interface. Now, we are almost done with the functionality part.
But the most important step is still left which is to bind the Interface and Repository together. After this step, we will be able to call Repository class methods through the interface. So, let’s do that.
Logout Multiple Auth Session From Other Devices in Laravel 9
Bind Interface and Repository Together
In order to bind the repository class and interface, we will require a service provider. Hence, come back to the terminal and create a provider class as shown below.
php artisan make:provider RepositoryServiceProvider
After creating this provider, we will bind the Interface and Repository class together.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Interfaces\PostInterface;
use App\Repositories\PostRepository;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
// Bind Interface and Repository class together
$this->app->bind(PostInterface::class, PostRepository::class);
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
Now, in the next step, we need to register this ServiceProvider class in the configuration.
Register Service Provider in Laravel
To register the service provider, you have to navigate to the config->app.php file in the project. Now, inside the providers array, add the created service provider as shown below.
'providers' => [
......
......
......
App\Providers\RepositoryServiceProvider::class,
],
So, the service provider is registered. Now, in the next step, we will be writing our controller logic inside the PostController.php file.
Create CRUD Functionality in Controller
We already created the PostController.php file. Here, in the first step, you will need to create a constructor and invoke the interface so that we can call the defined methods.
<?php
namespace App\Http\Controllers;
use App\Interfaces\PostInterface;
use Illuminate\Http\Request;
class PostController extends Controller
{
private $postInterface;
public function __construct(PostInterface $postInterface)
{
$this->postInterface = $postInterface;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$posts = $this->postInterface->getAllPosts();
return view('posts.index', compact('posts'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('posts.create');
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
try {
$post = $this->postInterface->createPost($request);
if ($post) {
return redirect()->route('posts.index')->with('success', 'Success! post is created');
} else {
return back()->with('failed', 'Failed! unable to create post');
}
} catch (Exception $e) {
return back()->with('failed', $e->getMessage());
}
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
try {
$post = $this->postInterface->getPostById($id);
if ($post) {
$isView = true;
return view('posts.create', compact('post', 'isView'));
} else {
return redirect('posts.index')->with('failed', 'Failed! no post found');
}
} catch (Exception $e) {
return redirect('posts.index')->with('failed', $e->getMessage());
}
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
try {
$post = $this->postInterface->getPostById($id);
if ($post) {
$isEdit = true;
return view('posts.create', compact('post', 'isEdit'));
} else {
return redirect('posts.index')->with('failed', 'Failed! no post found');
}
} catch (Exception $e) {
return redirect('posts.index')->with('failed', $e->getMessage());
}
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
try {
$post = $this->postInterface->updatePost($request, $id);
if ($post) {
return redirect()->route('posts.index')->with('success', 'Success! post is updated');
} else {
return back()->with('failed', 'Failed! unable to update post');
}
} catch (Exception $e) {
return back()->with('failed', $e->getMessage());
}
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
try {
$post = $this->postInterface->deletePost($id);
if ($post) {
return redirect()->route('posts.index')->with('success', 'Success! post is deleted');
} else {
return back()->with('failed', 'Failed! unable to delete post');
}
} catch (Exception $e) {
return back()->with('failed', $e->getMessage());
}
}
}
Next, as per the requirement, you need to create a couple of blade files for rendering the content.
Laravel Mailgun Integration For Sending Email in Laravel 9
Create Views For CRUD Implementation Using Repository Design Pattern
Mainly, we needed three blade files as shown below. But, I have kept these blade files inside the post folder.
- master.blade.php
- create.blade.php
- index.blade.php
Therefore, the folder structure of views should be like this.
resources
|_____ Views
| |_____ master.blade.php
| |_____ posts
| | |_____ create.blade.php
| | |_____ index.blade.php
| |
After creating these blades, let’s start with the master blade. This will contain the bootstrap and the main section which will be extended further in the child blades.
<!doctype html>
<html lang="en">
<head>
<title>Laravel Repository Design Pattern | Programming Fields</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
</head>
<body>
<section class="py-4">
<h3 class="text-center fw-bold">Repository Design Pattern | Programming Fields</h3>
@yield('content')
</section>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"
integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.min.js"
integrity="sha384-7VPbUDkoPSGFnVtYi0QogXtr74QeVeeIs99Qfg5YCF+TidwNdjvaKZX19NZ/e6oz" crossorigin="anonymous">
</script>
</body>
</html>
Next, add the below snippet in the index.blade.php file. This will contain the listing of the posts with a create option.
@extends('master')
@section('content')
<div class="container my-5">
<div class="row">
<div class="col-xl-6">
@if (Session::has('success'))
<div class="alert alert-success d-flex align-items-center alert-dismissible fade show" role="alert">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Success:">
<use xlink:href="#check-circle-fill" />
</svg>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<div>
{{ Session::get('success') }}
</div>
</div>
@elseif (Session::has('failed'))
<div class="alert alert-danger d-flex align-items-center alert-dismissible fade show" role="alert">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img"
aria-label="Danger:">
<use xlink:href="#exclamation-triangle-fill" />
</svg>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
<div>
{{ Session::get('failed') }}
</div>
</div>
@endif
</div>
<div class="col-xl-6 text-end">
<a href="{{ route('posts.create') }}" class="btn btn-primary"> Create Post </a>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped table-light">
<thead>
<tr>
<th scope="col" width="10%">#</th>
<th scope="col">Title</th>
<th scope="col">Content</th>
<th scope="col" width="20%">Action</th>
</tr>
</thead>
<tbody>
@forelse ($posts as $post)
<tr>
<td>{{ $post->id }}</td>
<td scope="row">{{ $post->title ?? '-' }}</td>
<td>{{ $post->content ?? '-' }}</td>
<td>
<form action="{{ route('posts.destroy', $post->id) }}" method="POST">
@csrf
@method('DELETE')
<a href="{{ route('posts.show', $post->id) }}" class="btn btn-sm btn-info">View</a>
<a href="{{ route('posts.edit', $post->id) }}" class="btn btn-sm btn-success">Edit</a>
<button type="submit" class="btn btn-sm btn-danger">Delete</button>
</form>
</td>
</tr>
@empty
<tr>
<td scope="row" colspan="4">
<p class="text-danger text-center">No post found!</p>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection
The last one, we have the create blade. So, add the below snippet in that blade file.
@extends('master')
@section('content')
@php $view = $isView ?? false;
$edit = $isEdit ?? false;
@endphp
<div class="container my-5">
<div class="row">
<div class="col-xl-6 col-12 m-auto">
<form action="{{ $edit ? route('posts.update', $post->id) : route('posts.store') }}" method="POST" class="w-100">
@csrf
@if ($edit)
@method('PUT')
@endif
<div class="card shadow">
<div class="card-header">
<h4 class="card-title"> {{ $view ? 'View' : ($edit ? 'Update' : 'Create')}} Post</h4>
</div>
<div class="card-body">
<div class="form-group my-2">
<label for="title" class="form-label">Title <span class="text-danger">*</span></label>
<input type="text" name="title" @if ($view) disabled @endif class="form-control" id="title" required value="{{ old('title') ?? ($post->title ?? '') }}">
</div>
<div class="form-group my-2">
<label for="content" class="form-label">Content <span class="text-danger">*</span></label>
<textarea name="content" @if ($view) disabled @endif class="form-control" id="content" required>{{ old('content') ?? ($post->content ?? '') }}</textarea>
</div>
</div>
<div class="card-footer">
<a href="{{route('posts.index')}}" class="btn btn-secondary">Back</a>
@if (!$view)
<button type="submit" class="btn btn-primary">Save</button>
@endif
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
Lastly, you need to add the route for the post controller.
Add Routes For Post Controller
For adding the route, navigate to the web.php file and add the route as shown below.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
Route::resource('posts', PostController::class);
That’s it for the Repository Design Pattern in Laravel. Now, you can run the application to check the results.
Bottom Lines
Finally, we implemented the Repository Design Pattern in Laravel with an example of a CRUD application. This was just a basic demonstration of using the design pattern in Laravel projects. You can extend this to a more advanced level as per the project requirement. I hope this post will be helpful to you.
In case, of any issue or help regarding this post or any query, you can reach out to me by writing in the comment section below. I will try to resolve your issues at earliest.
Sidiki Sawadogo says
It is very very good!
Naveen Joshi says
Very Nice Explanation