Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send the password reset link via email using queue in laravel 5

I am using the ResetsPasswords trait by laravel to implement password reset. What I would like to achieve is to send the email using queue. Digging through the code I found the line below in function postEmail():

$response = Password::sendResetLink($request->only('email'), function (Message $message) {
            $message->subject($this->getEmailSubject());
        }); 

Digging further I notice that sendResetLink() function is implemented in a PasswordBroker class which in turn calls the function emailResetLink(). emailResetLink function returns the following:

return $this->mailer->send($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
            $m->to($user->getEmailForPasswordReset());

which I can simply change mailer->send to mailer->queue. Is they some better way to do it without modifying this non-project file?

like image 491
Fokwa Best Avatar asked Dec 22 '15 05:12

Fokwa Best


3 Answers

I know this has been answered but I found another way to queue password reset notification which I found much more simple. I've tested it on Laravel 5.3 and 6.x.

By default, password reset notification is implemented by Illuminate\Auth\Notifications\ResetPassword class. This class is instantiated in your User model in sendPasswordResetNotification method and passed to notify method of Illuminate\Notifications\Notifiable trait.

So, to queue password reset notification you can simply create new ResetPassword notification class via artisan make:notification ResetPassword and replace it's code with this:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;

class ResetPassword extends ResetPasswordNotification implements ShouldQueue
{
    use Queueable;
}

And now just override sendPasswordResetNotification method in your App\User class:

<?php

...
use App\Notifications\ResetPassword as ResetPasswordNotification;
...    

/**
 * Send the password reset notification.
 *
 * @param  string  $token
 * @return void
 */
public function sendPasswordResetNotification($token)
{
    $this->notify(new ResetPasswordNotification($token));
}
like image 148
fakemeta Avatar answered Nov 18 '22 03:11

fakemeta


This is where the Laravel container comes to the rescue. If you don't like the functionality of a core component then you can go ahead and override it fairly painlessly.

First things first you will need to create your own PasswordBroker:

namespace App\Auth\Passwords;

use Illuminate\Auth\Passwords\PasswordBroker as IlluminatePasswordBroker;

class PasswordBroker extends IlluminatePasswordBroker
{

    public function emailResetLink()
    {
        $view = $this->emailView;

        return $this->mailer->queue($view, compact('token', 'user'), function ($m) use ($user, $token, $callback) {
            $m->to($user->getEmailForPasswordReset());
            if (! is_null($callback)) {
                call_user_func($callback, $m, $user, $token);
            }
        });
    }

}

Change your namespace to whatever you want if you want to place it elsewhere in your app.

Since the service provider registering the service is a deferred service provider you will need to create your own provider to replace it. Probably the easiest way to do this is extend Illuminate\Auth\Passwords\PasswordResetServiceProvider with something like the following:

namespace App\Providers;

use App\Auth\Passwords\PasswordBroker;

class PasswordResetServiceProvider extends \Illuminate\Auth\Passwords\PasswordResetServiceProvider
{

    protected function registerPasswordBroker()
    {
        $this->app->singleton('auth.password', function ($app) {
            $tokens = $app['auth.password.tokens'];

            $users = $app['auth']->driver()->getProvider();

            $view = $app['config']['auth.password.email'];

            return new PasswordBroker(
                $tokens, $users, $app['mailer'], $view
            );
        });
    }

}

Finally in your config/app.php file remove Illuminate\Auth\Passwords\PasswordResetServiceProvider::class and add App\Providers\PasswordResetServiceProvider::class to your 'providers' array.

Laravel will now use your implementation of the PasswordBroker rather than the stock framework one and you don't have to worry about modifying framework code.

like image 32
marcus.ramsden Avatar answered Nov 18 '22 03:11

marcus.ramsden


In case you get "Call to a member function onQueue() on null" after trying to specify queue fakemeta's solution just specify the the queue you are targeting in the constructor of your class.

<?php
namespace App;

use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;

class ResetPasswordNotification extends ResetPassword implements ShouldQueue
{
    use Queueable;

    public function __construct()
    {
        $this->queue = "authentication";
    }
}

then use the notification facade to send your mail in the overriding method. The notify method also works

<?php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\ResetPasswordNotification;

class User extends Authenticatable
{
    public function sendPasswordResetNotification($token)
    {
        // either of the two work
        // $this->notify(new ResetPasswordNotification($token));
        \Notification::send($this, new ResetPasswordNotification($token));
    }
}
like image 1
Stephen Mudere Avatar answered Nov 18 '22 04:11

Stephen Mudere