iT邦幫忙

2021 iThome 鐵人賽

DAY 26
1

Laravel 支援信件寄送功能,可以經由各種服務發送信件,框架也預設有使用者登入時就寄送驗證信的功能,不過一開始並沒有串接上寄信的服務,要先設定過後才能用。

寄信設定

寄信的環境變數設定在 config/mail.php 中。

首先看到 mailers 設定,用於定義寄送的信件要經由何種服務轉送,包含 smtp ,AWS SES , Mailgun 等服務都可以設定。

'mailers' => [
    'smtp' => [
        'transport' => 'smtp',
         //...
    ],

    'ses' => [
        'transport' => 'ses',
    ],

    'mailgun' => [
        'transport' => 'mailgun',
    ],

    'postmark' => [
        'transport' => 'postmark',
    ],

    'sendmail' => [
        'transport' => 'sendmail',
        'path' => '/usr/sbin/sendmail -bs',
    ],

    'log' => [
        'transport' => 'log',
        'channel' => env('MAIL_LOG_CHANNEL'),
    ],

    'array' => [
        'transport' => 'array',
    ],

    'failover' => [
        //...
    ],
],

回看到最上面的 default ,就是用於指定預設用哪個 mailer 寄送信件。

'default' => env('MAIL_MAILER', 'smtp'),

接著是 from ,就是預設的寄件者的信箱與名稱,當寄信時若沒另外指定寄件者就會取這裡的值。

'from' => [
    'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
    'name' => env('MAIL_FROM_NAME', 'Example'),
],

最後是 markdown,如果寄信的模板選用 markdown 編譯,就會到 paths 底下的目錄找對應的模板,應用預設的 theme ,也就是 css 格式。

'markdown' => [
    'theme' => 'default',

    'paths' => [
        resource_path('views/vendor/mail'),
    ],
],

可以看到設定中很多是從 env 取值的,所以要先進行 .env 的設定

MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=test@example.com
MAIL_PASSWORD=password
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=test@example.com
MAIL_FROM_NAME="${APP_NAME}"

如果是用 sail 建立的專案,這邊已經預設好 MAIL_HOST=mailhog , mailhog 是個信件測試工具,將 mailhog 指定為寄件的 SMTP 的話 mailhog 就會攔截信件並呈現在網頁介面上。

Sail 預設的 mailhog 開在 http://localhost:8025/ 上,可以先打開看看,目前還沒有任何信件。

因為這邊是用 mailhog 進行測試,所以 .env 的設定只要有值就好,如果是正經的寄信
MAIL_USERNAMEMAIL_PASSWORD 就要填對應服務的帳號密碼。

MAIL_USERNAME=test@example.com
MAIL_PASSWORD=password

註冊驗證信

想讓使用者在註冊時收到驗證信,先到 User Model 進行設定

/app/Models/User.php

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\UserSetting;
use App\Models\Todo;

class User extends Authenticatable
{
    //...
}

可以看到雖然已經引入了 MustVerifyEmail ,但 User 其實還沒應用上。

就把 MustVerifyEmail 加上去吧。

@@ -11,7 +11,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
 use App\Models\UserSetting;
 use App\Models\Todo;
 
-class User extends Authenticatable
+class User extends Authenticatable implements MustVerifyEmail
 {
     use HasApiTokens, HasFactory, Notifiable;
     use SoftDeletes;

接著就能新申請一個帳號,發現通知寄送驗證信成功。

到 MailHog 看看

出現一筆紀錄,點進去就能看到信件的內容,有個按鈕可以驗證信箱。

寄送流程

先來看看 Laravel 預設的驗證信寄送流程。

當使用者註冊、建立成功之後,發出 Registered 事件,關於事件的運作之後再詳談。

app/Http/Controllers/Auth/RegisteredUserController.php

public function store(Request $request)
{
    $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|string|email|max:255|unique:users',
        'password' => ['required', 'confirmed', Rules\Password::defaults()],
    ]);

    $user = User::create([
        'name' => $request->name,
        'email' => $request->email,
        'password' => Hash::make($request->password),
    ]);

    $user->setting()->create();

    event(new Registered($user));   // 註冊成功事件

    Auth::login($user);

    return redirect(RouteServiceProvider::HOME);
}

Registered 事件發出後會被 SendEmailVerificationNotification 處理。

app/Providers/EventServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
    protected $listen = [
        Registered::class => [  //處理 Registered 事件
            SendEmailVerificationNotification::class,
        ],
    ];

    //...
    
}

找到 SendEmailVerificationNotification ,這裡會用 User Model 中的 sendEmailVerificationNotification 執行寄信,而這個方法在哪呢?

vendor/laravel/framework/src/Illuminate/Auth/Listeners/SendEmailVerificationNotification.php

<?php

namespace Illuminate\Auth\Listeners;

use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Auth\MustVerifyEmail;

class SendEmailVerificationNotification
{
    public function handle(Registered $event)
    {
        if ($event->user instanceof MustVerifyEmail && ! $event->user->hasVerifiedEmail()) {
            $event->user->sendEmailVerificationNotification();
        }
    }
}

就在我們剛剛應用的 MustVerifyEmail 底下

vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php

<?php

namespace Illuminate\Auth;

use Illuminate\Auth\Notifications\VerifyEmail;

trait MustVerifyEmail
{
    //...
    
    public function sendEmailVerificationNotification()
    {
        $this->notify(new VerifyEmail);
    }     
}

這裡的VerifyEmail 是用來產生信件的,包含信件的標題以及內容、寄件人等,所以我們要客製驗證信的話,就在 User Model 底下覆蓋 sendEmailVerificationNotification 方法,改成寄客製化的信件。

客製信件

建立信件

首先用指令產生用 Markdown 編寫的信件

sail artisan make:mail CustomVerifyMail --markdown=mail.custom.verify

先看到剛剛產生的 Mailable ,信件的實例都是繼承 Mailable ,並且要有 build 函式定義如何建立信件。

app/Mail/CustomVerifyMail.php

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class CustomVerifyMail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct()
    {
        //
    }
    
    public function build()
    {
        return $this->markdown('mail.custom.verify');
    }
}

這邊我們選擇用 markdown 格式編寫信件內容

return $this->markdown('mail.custom.verify');

mail.custom.verify 會產生在 config/mail.php 中定義的 markdown path 底下,預設是 views/vendor/mail

resources/views/mail/custom/verify.blade.php

@component('mail::message')
# Introduction

The body of your message.

@component('mail::button', ['url' => ''])
Button Text
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent

現在就可以用 CustomVerifyMail 寄送信件了,在 User Model 底下編寫覆蓋 sendEmailVerificationNotification 的方法。

<?php

namespace App\Models;

//...
use App\Mail\CustomVerifyMail;
use Illuminate\Support\Facades\Mail;

class User extends Authenticatable implements MustVerifyEmail
{
    use HasApiTokens, HasFactory, Notifiable;
    use SoftDeletes;
     
    //...

    public function sendEmailVerificationNotification()
    {
        Mail::to($this)->send(new CustomVerifyMail);
    }     
}

不過現在寄信的話可以看到樣式還是跟原本的信件一樣。

這篇到這邊有點長了,關於信件樣式跟內容的改動就明天再寫吧。


上一篇
權限
下一篇
信件樣式與內容編輯
系列文
Laravel 實務筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言