Laravel 5.4 入門系列 11. 經過郵件激活註冊用戶

使用 Mailtrap 測試郵件功能

Mailtrap 提供了簡單的測試郵件的服務,步驟以下:php

  1. 登陸網站 Mailtraphtml

  2. 註冊用戶laravel

  3. 註冊成功以後,會自動建立一個 demo,點進去以後就能夠看到配置信息數據庫

clipboard.png

只須要把上面的信息配置到對應的 .env 中便可:markdown

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

這樣,咱們在應用裏面發出去的郵件你都能在網站的 demo 中看到了。閉包

用戶激活功能實現

用戶激活功能實現思路

接下來談談要實現的用戶激活功能的基本過程:app

  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 中會收到三封:

clipboard.png

由於 bcc 是密送,因此發送給 ccman 的郵件中看不到 bccman

clipboard.png

在上述的配置字段中, 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>

用戶將會收到這樣的郵件:

clipboard.png

點擊連接以後,會向網站發送請求,路由進行指派:

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

顯示結果爲:

clipboard.png

也能夠定義純文本視圖:

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

clipboard.png

對應的視圖文件以下:

/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 參數來生成對應的視圖便可。具體使用方法能夠查看文檔。


參考資料:

相關文章
相關標籤/搜索