Laravel 5.4 入门系列 11. 通过邮件激活注册用户

使用 Mailtrap 测试邮件功能

Mailtrap 提供了简单的测试邮件的服务,步骤如下:

  1. 登录网站 Mailtrap

  2. 注册用户

  3. 注册成功之后,会自动创建一个 demo,点进去之后就可以看到配置信息

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

只需要把上面的信息配置到对应的 .env 中即可:

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=fc0ad24e593f34
MAIL_PASSWORD=aef271516bc810
MAIL_ENCRYPTION=null

这样,我们在应用里面发出去的邮件你都能在网站的 demo 中看到了。

用户激活功能实现

用户激活功能实现思路

接下来谈谈要实现的用户激活功能的基本过程:

  1. 创建数据表,保存用户激活信息;

  2. 提供激活选项给登录其未激活的用户;

  3. 用户点击激活之后,生成一个唯一的秘钥,保存到数据库;同时将秘钥以参数形式附在超链接的后面显示在用户邮箱正文中;

  4. 用户在邮箱中点击超链接,返回网站;

  5. 网站根据参数是否匹配以及是否过期(24小时)来判断用户是否激活成功;

创建数据表

因为激活的功能并不常用,将其单独放置于一张表中:

$ php artisan make:model UserActivation -m

编辑字段:

/database/migrations/2017_04_21_071142_create_user_activations_table.php
 public function up()
{
    Schema::create('user_activations', function (Blueprint $table) {

        $table->integer('user_id')->unsigned()->primary();
        $table->string('token')->unique();
        $table->boolean('active')->default(0);
        $table->timestamps();

        $table->foreign('user_id')
              ->references('id')
              ->on('users')
              ->onDelete('cascade');
    });
}

执行:

$ php artisan migrate

添加用户表与用户激活表的一对一关系

用户表与用户激活表为一对一关系:

/app/User.php
public function activations()
{
    return $this->hasOne(\App\UserActivation::class);
}

允许用户激活表批量赋值:

/app/UserActivation.php
protected $fillable= ['user_id','active','token'];

添加激活选项

首先,让登录且未激活的用户可以点击「激活」按钮:

/resources/views/layouts/nav.blade.php
@if (Auth::check())
    <a class="dropdown-item" href="#">{{ Auth::user()->name }}</a>
    <a class="dropdown-item" href="/logout">登出</a>
    @if (is_null(Auth::user()->activations) || Auth::user()->activations->active == 0)
        <a class="dropdown-item" href="/sendActiveMail">发送激活邮件</a>
    @endif
@else            

路由指派该请求:

Route::get('/sendActivationMail','RegistrationController@send');

在实现具体的方法之前,我们先来介绍下如何快速发送邮件。

快速发送邮件

先来看看 Laravel 提供的选项:

$message->from($address, $name = null); # 邮件的所有者
$message->sender($address, $name = null); # 邮件的实际传输人,若所有者与发送者为同一人,可省略
$message->replyTo($address, $name = null); # 作者建议回复的地址
$message->to($address, $name = null); # 收件人的地址
$message->cc($address, $name = null); # 收件人外,想让其他知道该事的人的地址
$message->bcc($address, $name = null); # 不想让 cc 的人看到你发送给了谁,那么就用 bcc 

$message->subject($subject); #主题
$message->priority($level);
$message->attach($pathToFile, array $options = []);

注意,邮件的格式要遵守 RFC2822 规范,否则可能报错。

from            =       "From:" mailbox-list CRLF
sender          =       "Sender:" mailbox CRLF
reply-to        =       "Reply-To:" address-list CRLF
to              =       "To:" address-list CRLF
cc              =       "Cc:" address-list CRLF
bcc             =       "Bcc:" (address-list / [CFWS]) CRLF

我们举一个简单的例子:

public function send()
{   
    
    \Mail::raw('test laravel blog email function',function ($message){
        $from = ['From: from@qq.com','fromman'];
        $to = ['To: to@qq.com','toman'];
        $cc = ['Cc: cc@qq.com','ccman'];
        $bcc = ['Bcc: bcc@qq.com','bccman'];
        $replyTo = ['Reply-To: replyto@qq.com','replytoman'];
        $sender = ['Sender: sender@qq.com','senderman'];
        $subject = 'Subject Subject';
        $message->to($to)
                ->from($from)
                ->sender($sender)
                ->cc($cc)
                ->bcc($bcc)
                ->replyTo($replyTo)
                ->subject($subject);
    });

    return redirect('/');
}

raw 方法发送纯文本给对方。同时,我们在传入的闭包函数中进行配置。在本例子中,将会给 tomanccmanbccman 三个人一人发送一封纯文本邮件,因此,我们在 Mailtrap 中会收到三封:

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

因为 bcc 是密送,所以发送给 ccman 的邮件中看不到 bccman

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

在上述的配置字段中, from 字段以预先定义好,这样就不需要每次都填写了。当然,如果使用了 $message->from 方法,就会覆盖配置。

/config/mail.php
'from' => [
    'address' => env('MAIL_FROM_ADDRESS', '"From: "demo@blog.com'),
    'name' => env('MAIL_FROM_NAME', 'Zen'),
],

激活功能实现

现在,我们实现可以发送激活邮件的功能了:

use App\UserActivation;

public function send()
{    
    // 生成唯一 token
    $token = bcrypt(auth()->user()->email.time());
    $user = auth()->user();
    
     // 发送邮件
    \Mail::send('emails.activation', compact('user', 'token'), function ($message) {
        $to = ['To: '.auth()->user()->email, auth()->user()->name];
        $subject = 'blog demo 请您激活账户';

        $message->to($to)
                ->subject($subject);
    });
    
    
     // 数据库保存 token
    if ($user->activations){
        $user->activations()->update(['token'=>$token]);
    } else {
        $user->activations()->save(new UserActivation([
            'token' => $token
        ]));
    }
   
    // 发送并保存成功,跳转到主页
    return redirect('/');
}

这次,我们使用的是 Mail::send 方法,该方法可以传入视图作为邮件内容,同时第二个参数里传入给视图的数据,接下来定义视图:

/resources/views/emails/activation.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <p>您好, {{ $user->name }} ! 请点击下面链接完成注册:</p>
    <a href="http://localhost:8000/activeAccount/?verify={{$token}}">激活链接</a>
    
</body>
</html>

用户将会收到这样的邮件:

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

点击链接之后,会向网站发送请求,路由进行指派:

Route::get('/activeAccount','RegistrationController@active');

最后,判断该 token 是否过期或者是否匹配,匹配则跳转到登录页面,否则跳转到主页。

use Carbon\Carbon;
public function active()
{
    $token = request('verify');
    $rs = UserActivation::where('token', $token)
                        ->whereBetween('updated_at', [Carbon::now()->subDay(), Carbon::now()]);
    if ($rs->exists()) {
        $rs->update(['active'=>true]);
        return redirect('/login');
    }

    return redirect('/');
}

功能已经实现了。我们稍微封装下代码吧:

/app/Http/Controllers/RegistrationController.php
public function send()
{
    $token = bcrypt(auth()->user()->email.time());
    $user = auth()->user();

    \Mail::send('emails.activation', compact('user', 'token'), function ($message) {
    
        $to = ['To: '.auth()->user()->email, auth()->user()->name];
        $subject = 'blog demo 请您激活账户';

        $message->to($to)
                ->subject($subject);
    });


    $user->addActivationsData($token);
   
   
    return redirect('/');
}

public function active()
{
    $token = request('verify');
    $rs = UserActivation::where('token', $token)
                        ->notExpired();

    if ($rs->exists()) {
        $rs->update(['active'=>true]);
        return redirect('/login');
    }

    return redirect('/');
}

addActivationsData 方法用于保存或更新 $token 字段:

/app/User.php
public function addActivationsData($token)
{   
    if ($this->activations) {
        $this->activations()->update(['token'=>$token]);
    } else {
        $this->activations()->save(new \App\UserActivation([
            'token' => $token
        ]));
    }
}

notExpired 方法用于判断 token 是否在 24 小时之内:

/app/UserActivation.php
use Carbon\Carbon;
public function scopeNotExpired($query)
{
    return $query->whereBetween('updated_at', [Carbon::now()->subDay(), Carbon::now()]);
}

使用 mailables 管理邮件

在刚才的例子中,我们使用的是 Mail 提供的方法来快速创建和发送邮件。实际上,Laravel 提供了管理不同类型邮件的方法。我们来快速了解下。

创建「欢迎」邮件类型

首先,我们来创建一个用于欢迎新用户的 mailables:

$ php artisan make:mail Welcome

首次使用时,会创建 app/Mail 目录。接下来,我们就可以实现给用户发送欢迎邮件的功能了。

首先,用户注册成功之后,发送一封欢迎邮件:

/app/Http/Controllers/RegistrationController.php
use App\Mail\Welcome;
public function store()
{
    ...
    
    auth()->login($user);

    \Mail::to($user)->send(new Welcome);

    return redirect()->home();
}

之前我们通过闭包的方式传递 fromto 等字段,实际上,也可以直接将 $user 实例直接传递给 to,这样会自动去识别 namefrom 字段。然后,send 方法传入 mailables 类即可。

如果使用 to($user) 可能会报错,因为不符合我们之前说的 RFC2822 规范。

配置 mailables

接下来,发送邮件的主要功能都可以在定义的 Welcome 类中实现了。

可以定义邮件的 blade 视图:

/app/Mail/Welcome.php
public function build()
{   
    return $this->view('emails.welcome');
}

显示结果为:

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

也可以定义纯文本视图:

/app/Mail/Welcome.php
public function build()
{   
    
    return $this->text('emails.welcome');
}

《Laravel 5.4 入门系列 11. 通过邮件激活注册用户》

对应的视图文件如下:

/resources/views/emails/welcome.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <h1>{{$name}}, 欢迎成为 Blog Demo 的会员</h1>
</body>
</html>

可以配置邮件的各种信息,跟之前的方式类似:

public function build()
{   
    $from = ['From: from@qq.com','fromman'];
    $to = ['To: to@qq.com','toman'];
    $subject = 'Subject Subject';

    return $this->text('emails.welcome')
                ->to($to)
                ->from($from)
                ->subject($subject);

}

可以传递变量给视图,第一种是直接在 view 里面传递(也可以使用 with):

/app/Mail/Welcome.php
public function build()
{   
    $from = ['From: from@qq.com','fromman'];
    $to = ['To: to@qq.com','toman'];
    $subject = 'Subject Subject';
    $user = auth()->user();
    return $this->view('emails.welcome',compact('user'))
                ->to($to)
                ->from($from)
                ->subject($subject);


}

/resources/views/emails/welcome.blade.php
<h1> {{$user->name }}, 欢迎成为 Blog Demo 的会员</h1>

另外一种方法是,在 Welcome 类中定义属性类型为「公共的」。这样该属性就会自动传递给视图:

/app/Http/Controllers/RegistrationController.php
\Mail::send(new Welcome);

/app/Mail/Welcome.php
public $user;

public function __construct(User $user)
{
    $this->user = $user;
}

最后,我们还可以使用 Markdown 的语法来写邮件:

$ php artisan make:mail MDWelcome --markdown="emails.md-welcome"

创建使用使用 --markdown 参数来生成对应的视图即可。具体使用方法可以查看文档。

参考资料:

    原文作者:心智极客
    原文地址: https://segmentfault.com/a/1190000009156097
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞