如何使用Laravel 5中的队列通过电子邮件发送密码重置链接

16

我正在使用Laravel的ResetPassword特性来实现密码重置。我希望能够使用队列发送电子邮件。在查看代码时,我发现在postEmail()函数中有以下代码:

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

进一步调查后,我注意到sendResetLink()函数是在PasswordBroker类中实现的,该类又调用了函数emailResetLink()。emailResetLink函数返回以下内容:

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

我只需将mailer->send更改为mailer->queue即可。是否有更好的方法可以在不修改此非项目文件的情况下完成?


vendor/laravel/framework/src/illuminate/Auth/Passwords/PasswordBroker.php - Pathros
3个回答

38

我知道这个问题已经有答案了,但我发现了另一种更简单的排队密码重置通知的方法。我在 Laravel 5.3 和 6.x 上测试过。

默认情况下,密码重置通知是由Illuminate\Auth\Notifications\ResetPassword类实现的。该类在您的User模型中的sendPasswordResetNotification方法中实例化,并通过Illuminate\Notifications\Notifiable特性的notify方法传递。

因此,要排队密码重置通知,您只需通过artisan make:notification ResetPassword创建新的ResetPassword通知类,并将其代码替换为以下内容:

<?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;
}

现在只需在您的 App\User 类中覆盖 sendPasswordResetNotification 方法:

<?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));
}

相关文档:https://laravel.com/docs/5.5/notifications#queueing-notifications - Lemmings19
指定队列:$this->notify((new ResetPasswordNotification($token))->onQueue('queue-name')); - Lemmings19
@fakemeta 这个在 Laravel 6 中也适用吗?我试图深入了解重置密码的代码,但对于我的小脑袋来说太复杂了。 - Agil
1
它在 Laravel 8 Breeze 认证中起作用,就像实现电子邮件验证一样,谢谢 - Ismynr
刚试了一下,在 Laravel 8 中使用 Breeze 认证,按照说明运行良好! - Igor
显示剩余2条评论

4
这就是Laravel容器发挥作用的地方。如果您不喜欢核心组件的功能,那么您可以相对轻松地进行覆盖。
首先,您需要创建自己的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);
            }
        });
    }

}

如果您想将其放置在应用程序的其他位置,请更改命名空间。

由于注册服务提供程序是一个延迟服务提供程序,因此您需要创建自己的提供程序来替换它。可能最简单的方法是使用以下内容扩展Illuminate\Auth\Passwords\PasswordResetServiceProvider

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
            );
        });
    }

}

最后在你的config/app.php文件中,删除Illuminate\Auth\Passwords\PasswordResetServiceProvider::class并将App\Providers\PasswordResetServiceProvider::class添加到你的'providers'数组中。

Laravel现在会使用你的PasswordBroker实现而不是框架自带的,你不必担心修改框架代码。


感谢@marcus.ramsden的贡献。我已经按照你的方法进行了操作,但是似乎无法弄清楚为什么它不起作用。我没有收到任何错误信息。看起来好像它仍然在调用父类的emailResetLink而不是被覆盖的方法。 - Fokwa Best
抱歉,我的示例有误,在您的服务提供者的注册部分应该是$this->app->singleton而不是$this->app->bind。此外,核心服务提供者注册事项是延迟提供者。我会立即更新这些内容。 - marcus.ramsden
再次感谢您的输入。$this->app->singleton 也不能解决问题。仍然调用父方法。 - Fokwa Best
1
@michel3vb,我认为这个应该可以解决问题 https://gist.github.com/jamesfairhurst/a30f034f6aeef45fe32f17e5588c1adf - James F
谢谢你在这方面发表意见@JamesF :) 你想把这个问题转化为特定于Laravel 5.2的问题吗?我会让这个问题变得特定于5/5.1版本,以跟随经纪人管理器的变化。 - marcus.ramsden
显示剩余5条评论

1
如果您在尝试指定队列后遇到“Call to a member function onQueue() on null”错误,请按照fakemeta的解决方案,在类的构造函数中指定您要针对的队列即可。
<?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";
    }
}

然后在重写方法中使用通知外观来发送电子邮件。notify方法也可以使用。
<?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));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接