Web Programming TutorialsLearn about Using Repositories and Services in Laravel 5

Learn about Using Repositories and Services in Laravel 5

Repositories are usually a common wrapper for your model and the place where you would write different queries to your database. A service on the other hand is a layer for handling all your application’s logic. It is useful to separate your application’s logic to a common service (service layer) so that your application starts growing, and you have only one class to maintain.

Also the concept of repositories and services ensures that you write reusable code and helps to keep your controller as simple as possible making them more readable.

What we will be building?

To illustrate the repository and services pattern, we’ll be building a simple blog CRUD application. This is a very simple example to describe the concept, but in reality, chances are you will be needing repositories and services for something more complex.

For the purpose of this tutorial, I’ll assume you’ve set up Laravel already.

Getting Started

Let’s set up our Model, Controller, and Migration. Thankfully, Laravel can handle this with a single command, using artisan run the following command:

1 php artisan make:model Post -m -c

Tip: The `-m` and `-c` flags will create a migration and controller file associated with the model for you.

In the database/migrations directory, delete the users and password migrations created by Laravel as we will not be needing it for this tutorial. Let’s edit the post migration to reflect the structure of our database.

<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('body');
            $table->timestamps();
        });
    }

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

Post Model
We need to ensure attributes from our post model that are mass assignable. Edit app/Post.php with the following code:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = ['title', 'body'];
}

Next, let’s run our migrations. Edit the .env file with your database configurations and then run:

1 php artisan migrate

Creating the Repository
Unfortunately, there’s no artisan command for creating repositories or services, you will have to do it manually. Head over to the app directory and create a repositories folder. In the repositories folder, create a PostRepository class and add the following code.

<?php

namespace App\Repositories;

use App\Post;

class PostRepository
{
  
  protected $post;

  public function __construct(Post $post)
  {
    $this->post = $post;
  }
    public function create($attributes)
  {
    return $this->post->create($attributes);
  }
  
  public function all()
  {
    return $this->post->all();
  }

  public function find($id)
  {
   return $this->post->find($id);
  }
  
  public function update($id, array $attributes)
  {
  return $this->post->find($id)->update($attributes);
  }
 
  public function delete($id)
  {
   return $this->post->find($id)->delete();
  }
}

We type-hint the Post model in the constructor of our class and perform a basic CRUD operation on our Post model. Don’t forget to add the namespace at the top of the class.

Creating the Service
In the app directory, create a services folder and add a PostService class to the folder. Edit the class you just created with the following code:

<?php

namespace App\Services;

use App\Post;
use App\Repositories\PostRepository;
use Illuminate\Http\Request;

class PostService
{
	public function __construct(PostRepository $post)
	{
		$this->post = $post ;
	}

	public function index()
	{
		return $this->post->all();
	}

            public function create(Request $request)
	{
             $attributes = $request->all();
         
             return $this->post->create($attributes);
	}
	return $post;
	}

	public function read($id)
	{
     return $this->post->find($id);
	}

	public function update(Request $request, $id)
	{
	  $attributes = $request->all();
	  
      return $this->post->update($id, $attributes);
	}

	public function delete($id)
	{
      return $this->post->delete($id);
	}
}

We inject the PostRepository dependency into the constructor of our PostService class. Like we said earlier, everything that has to do with our application logic goes into the service class so we can keep our controllers as thin as possible. In the PostService class, we define CRUD methods for our Post object and make several Eloquent queries using PostRepository.

Setting up our Routes
Our application will have five routes. Edit the route/web.php file with the following code:

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', 'PostController@index');
Route::post('/post', 'PostController@create');
Route::get('/post/{id}', 'PostController@read')->name('edit.post');
Route::put('/post/{id}', 'PostController@update')->name('update.post');
Route::delete('/post/{id}', 'PostController@delete')->name('destroy.post');

We make use of named routes here, which we shall be using shortly in our forms.

Controller Methods
In the app/Http/Controllers directory, edit the PostController class we created earlier with the following code:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Post;
use App\Http\Requests\PostRequest;
use App\Services\PostService;

class PostController 
{ 
	protected $postservice;

	public function __construct(PostService $postservice)
	{
		$this->postservice = $postservice;
	}
    public function index(){
       
    $posts = $this->postservice->index();
     
    return view('index', compact('posts'));
    }

    public function create(PostRequest $request)
    {
      
      $this->postservice->create($request);

      return back()->with(['status'=>'Post created successfully']);
    }

    public function read($id)
    {
       
       $post = $this->postservice->read($id);

       return view('edit', compact('post'));

    }

    public function update(PostRequest $request, $id)
    {

       $post = $this->postservice->update($request, $id);

     return redirect()->back()->with('status', 'Post has been updated succesfully');
    }

    public function delete($id)
    {
     $this->postservice->delete($id);

     return back()->with(['status'=>'Deleted successfully']);
    }
}

Since our PostService class already handles all of our application’s logic, all we need to do is inject the service into our controller’s constructor. We define CRUD methods in our controller as well and call their respective methods from the service class via the postservice property.

If you notice at the top of our controller, we imported a PostRequest class for validation purposes which we haven’t created yet. Using artisan, run the following command:

1 php artisan make:Request PostRequest

This will create a PostRequest class for us in the app\Http\Requests directory. Edit the authorize method to return true and add the validation rules to the rules() method. The PostRequest class should look like this:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class PostRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title'=> 'required|min:3',
            'body'=> 'required|min:5'
        ];
    }
}
       
  

Creating our Views
Before visiting any of our routes in the browser, let’s quickly create our views. Head over to resources/views and create an index.blade.php file. This view will contain the form for creating a post and a table for listing all the available posts.

<!DOCTYPE html>
<html>
   <head>
      <meta name="viewport" content="width=device-width">
      <title>Laravel Repositories and Services</title>
      <meta name="description" content="">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
      <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" crossorigin="anonymous">
   </head>
   <body>
      <div class="container">
         <div class="col-md-5">
            <h4 class="page-header">Laravel Repositories and Services </h4>
            @foreach ($errors->all() as $error)
            <p class="alert alert-danger">{{ $error }}</p>
            @endforeach 
            @if (session('status'))
            <div class="alert alert-success alert-dismissable">
               <a href="#" class="close" data-dismiss="alert" aria-label="close">x</a>
               {{ session('status')}}
            </div>
            @endif
            <form class="form-vertical" role="form" method="post" action="/post">
               {{csrf_field()}}
               <div class="form-group">
                  <input type="text" name="title" class="form-control" placeholder="Title">
               </div>
               <div class="form-group">
                  <textarea class="form-control" rows="5" name="body" class="form-control" placeholder="Content"></textarea>
               </div>
               <div class="form-group">
                  <button type="submit" class="btn btn-info">Submit Post</button>
               </div>
            </form>
            <table class="table table-bordered">
               <thead>
                  <tr>
                     <th>Title</th>
                     <th>Content </th>
                     <th>Edit</th>
                     <th>Delete</th>
                  </tr>
               </thead>
               @foreach($posts as $post)
               <tbody>
                  <tr>
                     <td>{{$post->title}}</td>
                     <td>{{$post->body}}</td>
                     <form action="{{route('edit.post', $post->id)}}" method="GET">
                        <td>
                           <p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-success btn-xs" ><span class="fa fa-pencil fa-fw"></span></button></p>
                        </td>
                     </form>
                     <form action="{{route('destroy.post', $post->id)}}" method="POST">
                        {{csrf_field()}}
                        {{method_field('DELETE')}}
                        <td>
                           <p data-placement="top" data-toggle="tooltip" title="Delete"><button class="btn btn-danger btn-xs" ><span class="fa fa-fw fa-trash"></span></button></p>
                        </td>
                     </form>
                  </tr>
               </tbody>
               @endforeach
            </table>
         </div>
      </div>
   </body>
</html>

We created a form for submitting a post and then using a foreach loop, we loop through the post object we passed to our view from the index() method in our controller to display all the available posts. Each post will have an edit and a delete button enclosed within their respective form tags with their actions set to their respective named routes as well.

Now, you can visit your browser to see your application.

Submit Post

In the resources/views folder, create an edit.blade.php file. This is the view that will be returned whenever we want to edit a post. Add the following code to the file you just created.

<!DOCTYPE html>
<html>
   <head>
      <meta name="viewport" content="width=device-width">
      <title>Laravel Repositories and Services</title>
      <meta name="description" content="">
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
      <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" crossorigin="anonymous">
   </head>
   <body>
      <div class="container">
         <div class="col-md-5">
            <h4 class="page-header">Laravel Repositories and Services </h4>
            @foreach ($errors->all() as $error)
            <p class="alert alert-danger">{{ $error }}</p>
            @endforeach 
            @if (session('status'))
            <div class="alert alert-success">
               {{ session('status')}}
            </div>
            @endif
            <form class="form-vertical" role="form" method="post" action="{{route('update.post', $post->id)}}">
               {{csrf_field()}}
               {{method_field('PUT')}}
               <div class="form-group">
                  <input type="text" name="title" class="form-control" placeholder="Title" value="{{$post->title}}">
               </div>
               <div class="form-group">
                  <textarea class="form-control" rows="5" name="body" class="form-control" placeholder="Content">{{$post->body}}</textarea>
               </div>
               <div class="form-group">
                  <button type="submit" class="btn btn-info">Update Post</button>
               </div>
            </form>
         </div>
      </div>
   </body>
</html>

Conclusion: –
We’ve learnt how to utilize the concept of repository and services in Laravel by building a simple blog application.

14 COMMENTS

  1. Another “hello world” article…
    What i should todo if there are different validation rules for CREATE and UPDATE actions?
    What if validation rules are different for different user roles?
    What if i need update more than one model while UPDATE action? For ex. Attachment model
    There are already hundreds articles about service layers but there is no article with bit deeper explanation.

    • It is not necessary you type hint PostRequest in all functions. You can create more custom formrequests as per need of validation. Service layer is not for validation in laravel, but to put logic which concerns after validation. Like index function, we can show posts which are by logged user or all, that logic goes to service layer.

  2. I agree with the above comment. First of all, I understand that this is just a silly example of creating repositories and services, but i think that many beginner programmers can think that this is the right way of creating a module for blogging, which of course it isn’t because when you create a REAL blogging application you would never use a column for storing the content of your post, especially when the column is string typed, in that case you’d better use a text column or something like that.
    Now, going straight to my point, I think most of the real life scenarios include more login than simply storing and retrieving data, I mean, it could be that a software needs to log a lot events before storing a post instance., it might be the case when you’d need to limit some kind of accessible information depending on the role that the current user has.
    I appreciate your help, and this posts but i completely agree with Roman’s comment. There isn’t actually an article that goes deeply with more complicated or REAL scenarios.

    • same question here (new to laravel), and trying to understand if (and where) to tell Laravel which implementation to choose for the interface I injected in constr of controller

  3. What is the purpose of a Repository like this? I’ve used it too in the past, but i am changing my mind because, as far as i can tell, i only end up rewriting Eloquent..

    • A repository should be used with an interface too, think of a repository as your data abstraction layer, now imagine the tutorial has a PostRepository interface located in namespace App\Repositories\Contracts and the concrete implementation was defined as Post in the following namespace App\Repositories\Eloquent and would implement your PostRepository interface.

      Within the app service provider, you would then bind the PostRepository interface to the Post repository class and in the Service class, inject an instance of the PostRepository interface.

      Now imagine many months have passed and for some reason it was decided that the project owner no longer wished to use Eloquent, they now want to use Mongo.

      In response, you would create a new post repository class in namespace App\Repositories\Mongo using the same interface, but within its methods, write specific code to access the data from Mongo. Then in the app service provider, change the binding to use the new repository class and suddenly without touching the service layer or your controller, your application is using the new data source.

      The point is, your service layer would not care about the data source, it only cares about the contract the interface promises your data layer will fulfil.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exclusive content

- Advertisement -

Latest article

21,501FansLike
4,106FollowersFollow
106,000SubscribersSubscribe

More article

- Advertisement -