Laravel Validation – The Right Way

In this Tutorial we will see how we can use Laravel to Validate the Data. So often we see that our Validation Rules are duplicated for Add and Edit and our Controller is cluttered with all the Validation Rules which makes managing the Application Difficult.We will see how we can make our Controller Lean by using Form Request and apply various Validation Rules provided by Laravel.

The Tutorial assumes that you have the Authentication setup in a Laravel 6 Version, although it should also work if you are using Laravel 5.

We are going to assume our Resource is Story. Our Users can add and edit their respective Stories and on the way we are going to Validate their Stories.

Let us start by adding a Model. We can use the following command

php artisan make:model Story -mfcr

This will create a model named as Story and the file will be created as App/Story.php. We have also passed 4 flags to our command.

  • m will create a Migration File for us in the folder database/migrations/
  • f will create a Factory for us in the folder database/factories/
  • c will create a Controller for us in the app/Http/Controllers/
  • r will make sure that our generated controller is Resource Controller, more on it later.

Now open the Migration File. This will have a up() method where we will define all columns for our table. In the file you will notice that Laravel assume our table name as stories. So typically a Story has a Title and it has a body. We will have a column for both. Also we will have a column named as active, which will be a radio button to determine if the Story is visible on the frontend or not. And we will also have a type column which will be a select dropdown for user to categorize their stories. And we will also need a user_id column to link Stories to their Users. So our up() method will look like this.

        Schema::create('stories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id')->index();
            $table->string('subject', 255);
            $table->longText('body');
            $table->string('type', 16);
            $table->boolean('active');
            $table->timestamps();
        });

Please see that Laravel has automatically defined id as the primary key. Also the timestamps() line will create column named as created_at and modified_at automatically. Laravel will also populate them accordingly when a row is added or edited.

Now we need to Run our migration. This will create the Table in our Database. You can run the migration using the following command:

php artisan migrate

Next we define all our Routes related to Story Resource. Open up the /routes/web.php and just add the following line

Route::resource('stories', 'StoryController');

This single line will create all the following Resources. Of course you can also create all these routes individually if you only need a few of them.

RequestURIAction
GET/storiesStoryController@index
GET/stories/createStoryController@create
POST/stories/ StoryController@store
GET/stories/{story}/edit/ StoryController@edit
PUT / PATCH/stories/{story} StoryController@update
DELETE/stories/{story{ StoryController@destroy
GET/stories/{story} StoryController@show

You can check all the routes generated using the following command:

php artisan route:list

Now open the Model at App\Story.php. Laravel expects you define mass assignment fields. You can define them by specifying$fillable array like below

protected $fillable = ['subject', 'body', 'type', 'active'];

Also we need to define the Relationships, which in our case is User hasMany Stories. We define this Relationship in our User Model. Please see that for the time being we are not defining the Inverse Relationship in our Story Model

    public function stories()  {

        return $this->hasMany(\App\Story::class);
    }

Now, we shift our focus to our controller at app/Http/Controllers/StoryController.php. If you open up the file you will see that all of the methods corresponding to the routes have already been defined. This is because we passed the flag -r when we created the Model

Lets begin by creating the Form. The corresponding method is create(). So just change the method to following:

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

Here we are just telling the Laravel to use the View File located at resources/views/create.blade.php. Now open up this file and here we will be building our Form. Please see that we are not using any Third Party Component to build Form although it would not make any difference even if you choose to do so.

First thing is we need to Open the Form and specify the action and method. To specify the action we can make use of route function as below

<form action="{{route('stories.store')}}" method="POST">
@csrf

Don’t forget the @csrf Blade Directive which will generated a hidden Token Field to protect from CSRF attacks.

Now we begin by defining the input field for title and textarea for body.

	   <div class="form-group">
	       <label for="subject">Subject</label>
	       <input type="text" name="subject" placeholder="Subject" class="form-control" />
	   </div>
	
	   <div class="form-group">
	       <label for="body">Body</label>
	       <textarea name="body" class="form-control"></textarea>
	   </div>

Nothing Fancy Here. Just some basic HTML. Next we define the dropdown for Type. We are going to give User Option to categorize their story as Short Story or Long Story. Lets define it like below. We will later make changes so that these options are coming from Model

       <div class="form-group">
           <label for="type">Type</label>

           <select name="type" class="form-control">
               <option value="">--Select--</option> 
               <option value="short">Short Story</option>
               <option value="long">Long Story</option>
           </select>
       </div>	

Last thing is we need a Radio Button to mark our Story as Active or Not. Add the following HTML.

      <div class="form-group">
          <legend>Active</legend>
          <div class="form-check">
              <input class="form-check-input" name="active" type="radio" value="1" />
              <label for="active" class="form-check-label">Yes</label>
          </div>
          <div class="form-check">
              <input class="form-check-input" name="active" type="radio" value="0" />
              <label for="active" class="form-check-label">No</label>
          </div>
      </div>

At the end very we add the Submit Button and then Close our Form

   <div>
       <button class="btn btn-primary">Add New Story</button>
   </div>
</form>

This completes our Form. Our entire View File looks like below:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Add Story</div>

                <div class="card-body">
                    <form action="{{route('stories.store')}}" method="POST">
                        @csrf

                        <div class="form-group">
                            <label for="subject">Subject</label>
                            <input type="text" name="subject" placeholder="Subject" class="form-control" />
                        </div>

                        <div class="form-group">
                            <label for="body">Body</label>

                            <textarea name="body" class="form-control"></textarea>
                        </div>

                        <div class="form-group">
                            <label for="type">Type</label>

                            <select name="type" class="form-control">
                                <option value="">--Select--</option> 
                                <option value="short">Short Story</option>
                                <option value="long">Long Story</option>
                            </select>
                        </div>

                        <div class="form-group">
                            <legend>Active</legend>
                            <div class="form-check">
                                <input class="form-check-input" name="active" type="radio" value="1" />
                                <label for="active" class="form-check-label">Yes</label>
                            </div>
                            <div class="form-check">
                                <input class="form-check-input" name="active" type="radio" value="0" />
                                <label for="active" class="form-check-label">No</label>
                            </div>
                        </div>
                        <div>
                            <button class="btn btn-primary">Add New Story</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

If you now go to http://127.0.0.1:8000/stories/create, you should see the Form. Next we will work on the Validation for our form.

For the time being, let us define our Validation such that all these fields are required. To do so open up the Controller and update the store() method like below:

    public function store(Request $request)
    {
        //
        $data = request()->validate([
            'subject' => 'required',
            'body' => 'required',
            'type' => 'required',
            'active' => 'required',
        ]);

        auth()->user()->stories()->create( $data );

        return redirect()->route('stories.index')->with('status', 'Story created');
 

Let us take a moment to see what is happening here. There are 3 steps. In the First Step we define all the Validation Rules. Now Laravel is very smart, if the Validation fails which means if any of the field is empty it will automatically redirect to the previous method and display all the Error Messages there. If the Validation Passes it will move to the 2nd step.

In the 2nd Step we are telling it to store the Validated Data. See how we are getting the Authenticated User and then getting the stories which we defined as a Relationship in User Model. This way we do not need to pass the user_id to the create() method. Laravel will do this automatically for us based on the relationship.

In the 3rd Step, we are just redirecting the User to the index() method along with the Status.

Alright, now lets go to http://127.0.0.1:8000/stories/create in your Browser and submit the Form without filling anything. You will see that you are again back to this Page. This is because the Step1 that we discussed above failed. However, you will not see any Error Messages. We will need to update our View to display those Error Messages below each Form Field. Laravel provides a very nice way to do this using the error Blade Directive.

           @error('subject')
           <span class="invalid-feedback" role="alert">
               <strong>{{ $message }}</strong>
           </span>
           @enderror

This will check if the subject field has any error and then it will automatically fetch the Error Message in $message Variable which we can then use to display the Error Message.

Another thing that we can do is to change the class of the input field when that field has the error. We can do it using below code which will add the class .is-invalid in case this field fails the Validation.

@error('subject') is-invalid @enderror

Our Subject Field now looks like below after the above 2 changes

       <div class="form-group">
           <label for="subject">Subject</label>
           <input type="text" name="subject" placeholder="Subject" class="form-control @error('subject') is-invalid @enderror" />
           @error('subject')
           <span class="invalid-feedback" role="alert">
               <strong>{{ $message }}</strong>
           </span>
           @enderror
       </div>

Apply the same changes to all the other Fields as well and then submit the Form without filling any fields. You will now see all the error messages below each of field.

Now fill the Subject Field and submit the Form. You will see that Laravel still shows Error under all the other Fields except for Subject Field. However now the Value that we entered in the Subject Field is gone. This is not User Friendly at all. We need to make sure that it is being filled correctly. Fortunately Laravel does have another Blade Directive known as old which we can use. This takes 2 parameters, the old Value and the Default Value. In case of Subject Field we can specify it like below:

value="{{ old('subject', '') }}"

Here subject is the Key and we are specifying second parameter as empty which will be shown if there is no Key present. Apply this directive to all the other Fields as well. Once applied our View File will look like below:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Add Story</div>
                <div class="card-body">
                    <form action="{{route('stories.store')}}" method="POST">
                        @csrf

                        <div class="form-group">
                            <label for="subject">Subject</label>
                            <input type="text" name="subject" placeholder="Subject" class="form-control @error('subject') is-invalid @enderror" value="{{ old('subject', '') }}"/>
                            @error('subject')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <label for="body">Body</label>
                            <textarea name="body" class="form-control @error('body') is-invalid @enderror">{{ old('body', '') }}</textarea>
                            @error('body')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <label for="type">Type</label>

                            <select name="type" class="form-control @error('type') is-invalid @enderror">
                                <option value="">--Select--</option>
                                <option value="short" {{ ( 'short' == old('type', '') ) ? 'selected' : '' }}>Short Story</option>
                                <option value="long" {{ ( 'long' == old('type', '') ) ? 'selected' : '' }}>Long Story</option>
                            </select>
                            @error('type')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <legend>Active</legend>
                            <div class="form-check @error('active') is-invalid @enderror">
                                <input class="form-check-input" name="active" type="radio" {{ ( 1 == old('active', '') ) ? 'checked' : '' }} value="1" />
                                <label for="active" class="form-check-label">Yes</label>
                            </div>
                            <div class="form-check">
                                <input class="form-check-input" name="active" type="radio" {{ ( 0 == old('active', '') ) ? 'checked' : '' }} value="0" />
                                <label for="active" class="form-check-label">No</label>
                            </div>
                            @error('active')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>
                        <div>
                            <button class="btn btn-primary">Add New Story</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

We have now Validated our Data and our First Step out of 3 Steps in store() is completed. Step 2 is easy. Just Fill in all the Fields and Submit the Form. You should see a blank Page. However, if you now look into your Database Table you will see there is 1 Record being inserted with the values that you filled. Also you will see that user_id has been automatically inserted belonging to the Authenticated User. The Reason that you see a blank Page is because in Step 3 we are redirecting to the index method. However this method is empty. So lets add code in controller and create a View File to complete our Addition Process.

To display the listing, we need to change the index() method of Controller. We need to fetch all the Records belonging to the Authenticated User. Additionally we will Sort the Record on the basis of id DESC. We can change our index() method like below:

    public function index()
    {
        //
        $stories = Story::where('user_id', auth()->user()->id)
            ->orderBy('created_at', 'DESC')
            ->get();
        return view('story.index', [
            'stories' => $stories
        ]);
    }

Additionally we are telling the Laravel to look for the View at /resources/views/story/index.blade.php. So lets create this file and display the Listing like below

@extends('layouts.app')

@section('content')

<div class="container">
	<div class="row">
		<h2>Stories</h2>
		<a href="{{ route('stories.create') }}">Add New Story</a>
	</div>

	@foreach( $stories as $story )
		<div class="row">
			<div class="col">{{ $story->subject }}</div>
			<div class="col">{{ $story->body }}</div>
			<div class="col">{{ ( $story->active == 1) ? 'Yes' : 'No' }}</div>
			<div class="col"> 
					<a href="{{ route('stories.edit', [$story ]) }}">Edit</a> 
			</div>
		</div>
	@endforeach
</div>
@endsection

The code is self explanatory. Open the URL http://127.0.0.1:8000/stories/ in Browser and you will see the listing. In next section, we will work on Edit Story Section.

If you click on the Edit Link, it will take you to the http://127.0.0.1:8000/stories/1/edit. For now it will be a blank page. Let us work on the Controller. Laravel has already provided us the $story to be edited in our edit method using the Binding. We just need to pass it to our view. So our edit method looks like

    public function edit(Story $story)
    {
        //
        return view('story.edit', [
            'story' => $story
        ]);        
    }

We will also update the update() method which is used to Update our Story. For now it will almost be a replica of create() method. Instead of Creating a New Story, we will be updating the given Story. So update() method looks like.

    public function update(Request $request, Story $story)
    {
        //
        $data = request()->validate([
            'subject' => 'required',
            'body' => 'required',
            'type' => 'required',
            'active' => 'required',
        ]);

        $story->update( $data );        
        return redirect()->route('stories.index')->with('status', 'Story updated');
    }

Now in the edit() method we told Laravel that our Edit Form will be located at resources/views/stories/edit.blade.php. Create this File. This will be very similar to Add Form with only difference being that we need to populate this Form with the existing value. Remember we used the old Blade Directive and passed the second parameter as empty. Here instead of passing it as empty we will pass the existing value from the Database.

So following code

value="{{ old('subject', '')}}" 

will become like

value="{{ old('subject', $message->subject)}}" 

Once you complete all the changes, your View File will look like below

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Edit Story</div>

                <div class="card-body">
                    <form action="{{route('stories.update', [$story])}}" method="POST">
                        @csrf
                        @method('PUT')

                        <div class="form-group">
                            <label for="subject">Subject</label>
                            <input type="text" name="subject" placeholder="Subject" class="form-control @error('subject') is-invalid @enderror" value="{{ old('subject', $story->subject) }}"/>
                            @error('subject')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <label for="body">Body</label>
                            <textarea name="body" class="form-control @error('body') is-invalid @enderror">{{ old('body', $story->body) }}</textarea>
                            @error('body')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <label for="type">Type</label>

                            <select name="type" class="form-control @error('type') is-invalid @enderror">
                                <option value="">--Select--</option>
                                <option value="short" {{ ( 'short' == old('type', $story->type) ) ? 'selected' : '' }}>Short Story</option>
                                <option value="long" {{ ( 'long' == old('type', $story->type) ) ? 'selected' : '' }}>Long Story</option>
                            </select>
                            @error('type')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div class="form-group">
                            <legend>Active</legend>
                            <div class="form-check @error('active') is-invalid @enderror">
                                <input class="form-check-input" name="active" type="radio" {{ ( 1 == old('active', $story->active) ) ? 'checked' : '' }} value="1" />
                                <label for="active" class="form-check-label">Yes</label>
                            </div>
                            <div class="form-check">
                                <input class="form-check-input" name="active" type="radio" {{ ( 0 == old('active', $story->active) ) ? 'checked' : '' }} value="0" />
                                <label for="active" class="form-check-label">No</label>
                            </div>
                            @error('active')
                            <span class="invalid-feedback" role="alert">
                                <strong>{{ $message }}</strong>
                            </span>
                            @enderror
                        </div>

                        <div>
                            <button class="btn btn-primary">Update Story</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Just check the action where Form is being submitted. Also see that we are using Method Spoofing to trick Browser into thinking that it is a PUT Request. Now reload the Browser at http://127.0.0.1:8000/stories/1/edit. You will see that Form is populated. If you update the Form and Submit the Story will be updated as well. You will also notice that all the Validation is also working fine.

If you have followed up till here you will see that our Listing, Add Form and Edit Form are working fine. However, by now there is a lot of duplicate code and we can clean up a bit. We see that we have 2 repeat our Form Fields in both Add and Edit Form. So lets clean up it a bit so that we have only Form that we can utilize in both Add and Edit Form.

Lets Create a Separate View File at the same level as edit.blade.php and call it form.blade.php

Now cut all the code where the Form Fields are defined from edit.blade.php and put it inside the form.blade.php. Do not copy the Opening Form Element as well as the Submit Button. Our form.blade.php will look like below

<div class="form-group">
    <label for="subject">Subject</label>
    <input type="text" name="subject" placeholder="Subject" class="form-control @error('subject') is-invalid @enderror" value="{{ old('subject', $story->subject) }}" />
    @error('subject')
    <span class="invalid-feedback" role="alert">
        <strong>{{ $message }}</strong>
    </span>
    @enderror
</div>

<div class="form-group">
    <label for="body">Body</label>
    <textarea name="body" class="form-control @error('body') is-invalid @enderror">{{ old('body', $story->body) }}</textarea>
    @error('body')
    <span class="invalid-feedback" role="alert">
        <strong>{{ $message }}</strong>
    </span>
    @enderror
</div>

<div class="form-group">
    <label for="type">Type</label>

    <select name="type" class="form-control @error('type') is-invalid @enderror">
        <option value="">--Select--</option>
        <option value="short" {{ ( 'short' == old('type', $story->type) ) ? 'selected' : '' }}>Short Story</option>
        <option value="long" {{ ( 'long' == old('type', $story->type) ) ? 'selected' : '' }}>Long Story</option>
    </select>
    @error('type')
    <span class="invalid-feedback" role="alert">
        <strong>{{ $message }}</strong>
    </span>
    @enderror
</div>

<div class="form-group">
    <legend>Active</legend>
    <div class="form-check @error('active') is-invalid @enderror">
        <input class="form-check-input" name="active" type="radio" {{ ( 1 == old('active', $story->active) ) ? 'checked' : '' }} value="1" />
        <label for="active" class="form-check-label">Yes</label>
    </div>
    <div class="form-check">
        <input class="form-check-input" name="active" type="radio" {{ ( 0 == old('active', $story->active) ) ? 'checked' : '' }} value="0" />
        <label for="active" class="form-check-label">No</label>
    </div>
    @error('active')
    <span class="invalid-feedback" role="alert">
        <strong>{{ $message }}</strong>
    </span>
    @enderror
</div>

Now we need to include this Form inside our edit.blade.php. Blade provides a include Directive which we can use like below

@include('stories.form')

This will include the Form inside the Edit View. Go ahead and open a Story and Edit it. It should update Correctly. Our edit.blade.php now looks much cleaner and has the below code

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Edit Story</div>

                <div class="card-body">
                    <form action="{{route('stories.update', [$story])}}" method="POST">
                        @csrf
                        @method('PUT')

                        @include('story.form')

                        <div>
                            <button class="btn btn-primary">Update Story</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Lets open the created.blade.php and remove the unwanted code and include our Form. Our Updated Create File will look like below

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Add Story</div>
                <div class="card-body">
                    <form action="{{route('stories.store')}}" method="POST">
                        @csrf

                        @include('story.form')
                        
                        <div>
                            <button class="btn btn-primary">Add New Story</button>
                        </div>
                    </form>

                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Open up the Browser at http://127.0.0.1:8000/stories/create. You will see an error that there is undefined Variable story. So what happened. If you open up the Form, our Subject Field looks like below

    <input type="text" name="subject" placeholder="Subject" class="form-control @error('subject') is-invalid @enderror" value="{{ old('subject', $story->subject) }}" />

You see that we are passing $story as the second variable to the old directive. This worked for the Edit where it picked the default value from the Database but while creating a New Story we do not have this Variable and thus we are getting the Error. Fortunately there is a workaroud in Laravel. You can create the $story Variable for the New Story in the create() method like below

$story = new Story;

This will create a New Model Instance which will have all the attributes of Story Model. We just need to pass it to view. So our create() method will now look like

    public function create()
    {
        //
        $story = new Story;
        return view('story.create', [
            'story' => $story,
        ]);
    }

Go ahead and reload the URL and you will see that Error is gone and you are able to Add a New Story. So we have significantly reduce the redundant code and now any changes that we will have to make to our Story Form we will only need to make it in one place at form.blade.php

Now the other thing we can improve upon is the Validation Code. We are writing the same validation Code in both our store() and update() method. Moreover all Validation Logic is written in Controller which isn’t ideal. Now Laravel provides a way to overcome this. Enter Form Request. Form requests are custom request classes that contain validation logic. You can read more about Form Request in Docs

We will Name our Form Request as StoryRequestand we can create it using below command

php artisan make:request StroyRequest

This will create a New File at app/Http/rquests/StoryRequest.php and will hold all our Validation Logic. If you open up this File it will have 2 inbuilt methods. authorize() and rules(). First Method returns a Flag which determines whether the User is authorized to make this Request or not. It will be returning false by default. Change this to return true.

Second Method rules() returns an array which define the Validation Rules that should apply. Please see that these Validation Rules will apply to both Add and Edit Request. Move your Validation Rules from Controller to this File. Our rules method would look like below:

    public function rules()
    {
        return [
            'subject' => 'required',
            'body' => 'required',
            'type' => 'required',
            'active' => 'required',
        ];
    }

In the Controller you will just need to include this Form Request. You can also remove all the reference to Illuminate\Http\Request and replace them with StoryRequest. You can remove all the Validation Rules from the Controller. In the end your Controller would look like below

<?php

namespace App\Http\Controllers;

use App\Story;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\StoryRequest;

class StoryController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
        $stories = Story::where('user_id', auth()->user()->id)
            ->orderBy('created_at', 'DESC')
            ->get();
        return view('story.index', [
            'stories' => $stories
        ]);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
        $story = new Story;
        return view('story.create', [
            'story' => $story,
        ]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @return \Illuminate\Http\Response
     */
    public function store( StoryRequest $request)
    {
        //
        auth()->user()->stories()->create( $request->all() );

        return redirect()->route('stories.index')->with('status', 'Story created');
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Story  $story
     * @return \Illuminate\Http\Response
     */
    public function show(Story $story)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Story  $story
     * @return \Illuminate\Http\Response
     */
    public function edit(Story $story)
    {
        //
        return view('story.edit', [
            'story' => $story
        ]);        
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \App\Story  $story
     * @return \Illuminate\Http\Response
     */
    public function update(StoryRequest $request, Story $story)
    {
        //
        $story->update( $request->all() );        
        return redirect()->route('stories.index')->with('status', 'Story updated');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Story  $story
     * @return \Illuminate\Http\Response
     */
    public function destroy(Story $story)
    {
        //
    }
}

Our Controller looks much much cleaner than it looked before we introduced Form Request. Go ahead and try to create a New Story without adding any Fields and you will see that all Validation Rules specified in the Form Request are triggered correctly and the Error Messages are also displayed. The Validation Logic will also work while Editing a Story.

Now let us shift our attention a bit towards Validation. So far we have only used the required Validation Rule. However, there are multiple Validation Rules that are provided with Laravel.

Let us say we want to make sure that our subject should have a minimum of 10 Chars and maximum of 200 chars. We can define these rules using the inbuilt min and max Rules. If you want to apply multiple Validation Rules to a field, instead of passing the string you can pass all the Rules in an array. So Validation Rules for subject will look like

'subject' => ['required', 'min:10', 'max:200'],

Similarly for Body, we can define the minimum Length of Body Required is 50 chars. This can be reflected by using the below code

'subject' => ['required', 'min:10', 'max:200'],

Go ahead and Submit the Form using appropriate values and you will see these Validation will kick in along with appropriate error messages. You can check the other Validation Rules in Docs and apply any of them to a particular Field.

Now let us go a step ahead. Suppose we want to apply Conditional Validation. Let us say we want the Max characters in the Body to be capped to 200 when the Type of the Story is short. Well we can define such validation using withValidator method by using a Closure

    public function withValidator( $validation ) {
        $validator->sometimes('body', 'max:200', function ($input) {
            return $input->type == 'short';
        });
    }

We can also pass the Closure to the array while defining the Validation Rule for a given Field. Suppose we do not want the User to choose to choose subject as Subject. We can easily do it using the Closure as below:

            'subject' => [
                'required', 'min:10', 'max:200',
                function ($attribute, $value, $fail) {
                    if ($value === 'Dummy Subject') {
                        $fail($attribute . ' is invalid.');
                    }
                },
            ],

This will also throw the Error Message as Subject is Invalid.

One of the most commonly used Validation Rule is Unique. Suppose we want to make the Subject of our Story as Unique. We can do that by passing following line to the subject Validation Rules

Rule::unique('stories')

This will make sure that a Validation Error is thrown if we try to use the Same Subject. However if we now try to Edit a Story and try to update the Body without changing the Subject, you would not be able to do so. It will throw an error that Subject is not unique. In order to overcome this you need to tell Laravel to ignore the ID that we are editing. You can get the current ID using following code

$ignoreId = $this->route('story.id');

You can then change the Unique Validation Rule as

Rule::unique('stories')->ignore( $ignoreId)

Last thing that we would like to cover in this Topic is Customizing the Error Messages. When you do not enter a Subject, you get the message as The subject field is required. What if you want to change it to something else like Please enter Subject. Laravel provides an easy way to do this.

You need to create a messages() method in your Form Request. This will return an array. The Key will be field.error and the Value will be the Error Message that you want to display. So if you want to change the Error Message that Subject is required then the Key will be subject.required. So our message() method will look like

    public function messages()
    {

        return [
            'subject.required' => 'Please enter Subject',
        ];
    }    

Instead of passing the Subject in the Error Message you could also pass the :attribute and Laravel will automatically replace it. This gives us the flexibility of changing Error Message of Required for all the fields. Go ahead and try this.

    public function messages()
    {

        return [
            'required' => 'Please enter :attribute',
        ];
    }  

This brings us to the end of this Topic. Hope you had a chance to learn something new. All the code for this Tutorial is available on the GIT.

You can also check next part of this Tutorial where we write Test Cases of the given functionality and cover various scenarios