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_USERNAME
跟 MAIL_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);
}
}
不過現在寄信的話可以看到樣式還是跟原本的信件一樣。
這篇到這邊有點長了,關於信件樣式跟內容的改動就明天再寫吧。