Laravel form requests are special classes that extend the functionality of regular request classes, enabling advanced validation features. Form requests also help to keep your controller actions a lot cleaner, because you can move all your validation logic to the form request class. Another benefit is that it allows you to filter requests before they reach your controller actions.
In this guide, we’ll implement a password verification step to request that a user confirms their password before accessing an admin area. This practice works as a double check and provides your application an extra layer of security.
To follow up with this guide, you’ll need a working Laravel 5.6+ application with the built-in Laravel authentication set up. Please check the official documentation for details on how to set this up.
We are going to start by setting up a user’s edit profile page.
At the time of writing this tutorial, artisan
command utility does not generate views so we’ll need to create the view manually.
Create the file resources/views/profile/edit.blade.php
and add the following code.
@extends('layouts.app')
@section('content')
<div class="container">
@if (session('info'))
<div class="row">
<div class="col-md-12">
<div class="alert alert-success alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ session('info') }}
</div>
</div>
</div>
@elseif (session('error'))
<div class="row">
<div class="col-md-12">
<div class="alert alert-danger alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
{{ session('error') }}
</div>
</div>
</div>
@endif
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Update Profile</div>
<div class="panel-body">
<form class="form-horizontal" method="POST" action="{{ route('profile.update', ['user' => $user]) }}">
{{ csrf_field() }}
{{ method_field('PUT') }}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input id="name" type="text" class="form-control" name="name" value="{{ $user->name }}">
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password" class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input id="password" type="password" class="form-control" name="password">
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<label for="password-confirm" class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input id="password-confirm" type="password" class="form-control" name="password_confirmation">
</div>
</div>
<div class="form-group{{ $errors->has('current_password') ? ' has-error' : '' }}">
<label for="current-password" class="col-md-4 control-label">Current Password</label>
<div class="col-md-6">
<input id="current-password" type="password" class="form-control" name="current_password">
@if ($errors->has('current_password'))
<span class="help-block">
<strong>{{ $errors->first('current_password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Update
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
In our “edit profile” page, we check for an info
and error
flash message and display it to the user.
It has a name
, password
, password_confirmation
, and current_password
field.
The way we want it to work is whenever a user makes a change, they have to provide the correct current_password
field to commit the update to the database.
The password
and password_confirmation
fields will allow the user to change their password. If they are both left empty, the user’s current password will be retained and no changes will be made to their stored password.
The major players in our view are the password
, password_confirmation
, and current_password
fields.
As for the name
field, it serves as an example to expand upon and add more fields for your case.
Now onto the most crucial part of this tutorial.
Execute the following command to create a form request.
- php artisan make:request UpdateProfile
The above command will create a file named app/Http/Requests/UpdateProfile.php
.
All code changes in this section will be made to this file.
The first thing we need to do is alias Laravel’s Hash facade before the class declaration.
use Illuminate\Support\Facades\Hash;
Next, we need to return true
from our authorize
method since we are not performing authorization in our form request.
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
Our rules method will return the array outlining the validation rules for this request.
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|max:255',
'password' => 'nullable|required_with:password_confirmation|string|confirmed',
'current_password' => 'required',
];
}
The name
and current_password
rules are self-explanatory.
The password
rules states that the password will be confirmed using the confirmed
declaration.
It also declares required_with:password_confirmation
which means if the user provides a password confirmation, they should also provide a password.
These validation rules will be checked automatically upon every request once we type-hint it in our controller action(which we’ll do later).
The last thing we need to do is declare a withValidator
method on our request which is passed the fully constructed validator instance before any of the validation rules fire.
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
// checks user current password
// before making changes
$validator->after(function ($validator) {
if ( !Hash::check($this->current_password, $this->user()->password) ) {
$validator->errors()->add('current_password', 'Your current password is incorrect.');
}
});
return;
}
Inside our withValdator
method, we have added an after
hook, a function which will be executed after all the validation checks have been made.
In our after
hook, we have compared the user’s provided password with their password set in the database.
The $this->current_password
gives us the current_password
form field value whereas Laravel allows us to access the currently authenticated user using $this->user()
so $this->user()->password
gives us the user’s hashed password saved in the database.
The two passwords are compared using the Hash
facade’s check
method.
If the hash check fails, an error is added to the validator with the key current_password
using $validator->errors()->add('current_password', 'Your current password is incorrect.')
.
Here is our complete UpdateProfile
form request.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Hash;
class UpdateProfile 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 [
'name' => 'required|string|max:255',
'password' => 'nullable|required_with:password_confirmation|string|confirmed',
'current_password' => 'required',
];
}
/**
* Configure the validator instance.
*
* @param \Illuminate\Validation\Validator $validator
* @return void
*/
public function withValidator($validator)
{
// checks user current password
// before making changes
$validator->after(function ($validator) {
if ( !Hash::check($this->current_password, $this->user()->password) ) {
$validator->errors()->add('current_password', 'Your current password is incorrect.');
}
});
return;
}
}
To use our form request, we’ll need to type-hint it in our controller action.
Execute the following command to generate the profile controller.
- php artisan make:controller ProfileController
Open the file app/Http/Controllers/ProfileController.php
and add the following controller actions.
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the form for editing the specified resource.
*
* @param \App\User $user
* @return \Illuminate\Http\Response
*/
public function edit(Request $request, User $user)
{
// user
$viewData = [
'user' => $user,
];
// render view with data
return view('profile.edit', $viewData);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\User $user
* @return \Illuminate\Http\Response
*/
public function update(UpdateProfile $request, User $user)
{
// form data
$data = $request->all();
$user->update($data);
return redirect(route('profile.edit', ['user' => $user]))
->with('info', 'Your profile has been updated successfully.');
}
The profile controller’s constructor sets the auth
middleware to make sure a user is logged in before editing his/her profile.
The edit
action serves the view with the view data whereas the update
action updates the user profile and redirects back to the edit profile page with the corresponding flash message.
Notice the signature of the edit
action where we have type-hinted the UpdateProfile
request which is all we need to do to fire the validations inside our UpdateProfile
form request.
You will also need to alias the form request and the user model before the controller class declaration.
use App\Http\Requests\UpdateProfile;
use App\User;
Open the file app/routes/web.php
and add the following code to tie in the controller actions.
Route::get('/profile/{user}/edit', 'ProfileController@edit')->name('profile.edit');
Route::put('/profile/{user}', 'ProfileController@update')->name('profile.update');
Based on the validation rule we added earlier to our form request, it is possible for a null
password to pass through.
Under no circumstances would a user(or an application developer) want their password to be set to null
or an empty string.
To make sure a user password is set only when they provide one, we are going to use Eloquent ORM’s mutators.
Open the file app/User.php
and add the following code.
// Only accept a valid password and
// hash a password before saving
public function setPasswordAttribute($password)
{
if ( $password !== null & $password !== "" )
{
$this->attributes['password'] = bcrypt($password);
}
}
Eloquent mutators have to follow the naming scheme set<camel-cased-attribute-name>Attribute
.
Since we are declaring a mutator for the password
attribute hence we have named the mutator setPasswordAttribute
.
The mutator function is passed the value being set which in our mutator is the $password
variable.
In our mutator, we check if the $password
variable is not null or an empty string and set it in our model using $this->attributes['password']
.
Also note the password is hashed before saving so we do not have to do it elsewhere in our application.
The default Laravel Auth/RegisterController
and Auth/ResetPasswordController
also hash the password before persisting it to the database so we need to update the create
and resetPassword
method in the respective controllers after declaring the above mutator.
Open the file app/Http/Controllers/Auth/RegisterController.php
and add the following code.
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => $data['password'],
]);
}
Open the file app/Http/Controllers/Auth/ResetPasswordController.php
and add the following code.
/**
* Reset the given user's password.
*
* @param \Illuminate\Contracts\Auth\CanResetPassword $user
* @param string $password
* @return void
*/
protected function resetPassword($user, $password)
{
$user->password = $password;
$user->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
$this->guard()->login($user);
}
For the ResetPasswordController
, you will also need to alias the respective classes used before the class declaration.
use Illuminate\Support\Str;
use Illuminate\Auth\Events\PasswordReset;
We are all done and our password verification works as expected.
In this guide, we saw how to implement an additional password verification step to assert that a user is authorized to access an admin area. We’ve seen how to create and set up form requests to implement form validation within a Laravel application.
For more information about validation and form requests, you can check the official Laravel documentation.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!