Sep 23, 2013
Table of contents:
A couple of weeks ago I looked at how to create registration and authentication functionality in Laravel 4. Registering and authenticating are two of the fundamental features of a social application.
The third crucial component of this user interaction cycle is allowing users to be able to request an email to reset their password. This is one of those features that is so easy to overlook when developing an application, but your users will soon let you know about it if you forget it.
Fortunately Laravel 4 makes setting up this functionality incredibly easy and so this will be our focus for this week’s tutorial.
If you remember back to Setting up your first Laravel 4 Model, I briefly touched upon the remindable interface. It is this interface that enables Laravel’s reminder functionality to work out of the box.
So before we begin, ensure that your model implements the interface like this:
class User extends Magniloquent implements UserInterface, RemindableInterface
{
}
I’m sure you have reset your password on an online service in the past so you’ll be well aware of how this kind of thing works, but just for clarity, I will talk you through how it works in Laravel.
As you can see from the process above, it is relatively simple to create a system to allow users to reset their password, especially seeing as though Laravel does all of the heavy lifting for us.
However, we are still going to have to create a few things in order for the process to work:
Table migration We need a way of being able to store the reminder tokens in the database. Polluting the user table wouldn’t be a good idea, so Laravel can automatically generate the migration for the table for you.
Routes We will need a couple of routes to be able to GET the forms and POST the results to.
Password Controller We are going to need to use a couple of Controller methods to display forms and accept POST requests so it makes sense to keep them all together in the same Controller.
Reset Views And finally, we need two views. The first one is to display the form to request the reminder email, and the second one is to display the form that accepts the new password.
As I mentioned above, Laravel ships with much of this functionality all ready for you to implement. One big time saver is that you can automatically create the migration using the artisan
command line interface. This is fantastic because it means you don’t have to think about the schema that you’re going to need.
To create the migration, simply run the following in Terminal:
php artisan auth:reminders
This should create the following migration:
use Illuminate\Database\Migrations\Migration;
class CreatePasswordRemindersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create("password_reminders", function ($t) {
$t->string("email");
$t->string("token");
$t->timestamp("created_at");
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop("password_reminders");
}
}
Finally, run the migration:
php artisan migrate
So the first thing we need to do is to create a form that will allow a user to enter her email address to request a reset link.
First, create a new route in your routes.php
file:
Route::get("password/reset", [
"uses" => "PasswordController@remind",
"as" => "password.remind",
]);
Here I’m simply setting a GET route that is associated with a method on the Controller.
Next, create a new Controller file called PasswordController.php
and copy the following code:
class PasswordController extends BaseController
{
public function remind()
{
return View::make("password.remind");
}
}
As you can see from the method above, all we have to do is to return a View file.
So under your views directory, create a new folder called password
and a new view filed called remind.blade.php
and copy the following code:
@if (Session::has('error'))
{{ trans(Session::get('reason')) }}
@elseif (Session::has('success'))
An email with the password reset has been sent.
@endif
{{ Form::open(array('route' => 'password.request')) }}
<p>{{ Form::label('email', 'Email') }}
{{ Form::text('email') }}</p>
<p>{{ Form::submit('Submit') }}</p>
{{ Form::close() }}
If you remember back to my post on Laravel 4 forms the code above should look familiar. Basically the code above will generate a form with an email input field and a submit button. When the form is submitted, it with be POSTed to the password.request
route.
The if...else
statement above the form will display any errors or a confirmation if the form was been submitted correctly.
Next we need to set up another route to POST the form to and a method on the Controller to send the email.
Back in your routes.php
file, copy the following new route:
Route::post("password/reset", [
"uses" => "PasswordController@request",
"as" => "password.request",
]);
Now, in your PasswordController.php
file, add the following new method:
public function request()
{
$credentials = array('email' => Input::get('email'), 'password' => Input::get('password'));
return Password::remind($credentials);
}
This super simple method uses Laravel’s input reminder functionality to add the email to the reminder table we created earlier and send the email. You might get an exception saying that you haven’t set up your sender address yet. To fix this, go to app/config/mail.php
and fill in the relevant details.
Of course to actually send the email you will need to set up an SMTP server. Personally I like SendGrid. In a future tutorial I will do a much deeper dive on setting up email related stuff in your Laravel application.
If you want to see the template for the default email, it is stored under app/views/auth/reminder.blade.php
. You are of course free to change this, just ensure you leave the URL tag intact.
Next we will create the form that the user will land on after clicking the reset link in the email.
First create a new route:
Route::get("password/reset/{token}", [
"uses" => "PasswordController@reset",
"as" => "password.reset",
]);
This route requires that the secret token is set in the URL. This will be set automatically in the reminder email.
Next add the following method to your Password Controller file to request the correct View:
public function reset($token)
{
return View::make('password.reset')->with('token', $token);
}
Notice how this method will accept the token from the URL and assign it the View.
And finally, create another View under app/views/password
called reset.blade.php
and copy the following code:
@if (Session::has('error'))
{{ trans(Session::get('reason')) }}
@endif
{{ Form::open(array('route' => array('password.update', $token))) }}
<p>{{ Form::label('email', 'Email') }}
{{ Form::text('email') }}</p>
<p>{{ Form::label('password', 'Password') }}
{{ Form::text('password') }}</p>
<p>{{ Form::label('password_confirmation', 'Password confirm') }}
{{ Form::text('password_confirmation') }}</p>
{{ Form::hidden('token', $token) }}
<p>{{ Form::submit('Submit') }}</p>
{{ Form::close() }}
Again this is a relatively simple form. First we display any errors at the top. Then we create fields so the user can enter their email address and choose a new password.
Notice how I’ve also set the token as a hidden field and I’ve also set it as a parameter in the action route. The hidden token is used to validate against the database and the URL parameter is used as a requirement of the route.
And finally we can create the route and method to actually update the User’s password.
Firstly create the POST route:
Route::post("password/reset/{token}", [
"uses" => "PasswordController@update",
"as" => "password.update",
]);
Next create the method on the Password Controller:
public function update()
{
$credentials = array('email' => Input::get('email'));
return Password::reset($credentials, function($user, $password)
{
$user->password = Hash::make($password);
$user->save();
return Redirect::to('login')->with('flash', 'Your password has been reset');
});
}
Again this uses some Laravel trickery to do the heavy lifting. Laravel will attempt to reset the password. If it is successful, a User instance and the password are sent to the closure so you can run the update.
Next I’m simply redirecting to the login page with a flash message.
The reset method on the Password class will automatically deal with the heavily lifting of validating the request. It will check to ensure that a valid token has been sent and it will check the credentials and that the passwords match for you.
If there is an error with what the user entered, Laravel will automatically redirect back to the form and set an error message with a reason in the Session.
And there you have it, very simply password reminder and reset functionality almost straight out of the box from Laravel. One of the beautiful things about Laravel 4 is it automatically deals with common bits of functionality like this for you, so you don’t have to reinvent the wheel on every project.
This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.