信件

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', '[email protected]'),
    '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
[email protected]
MAIL_PASSWORD=password
MAIL_ENCRYPTION=null
[email protected]
MAIL_FROM_NAME="${APP_NAME}"

如果是用 sail 建立的专案,这边已经预设好 MAIL_HOST=mailhog , mailhog 是个信件测试工具,将 mailhog 指定为寄件的 SMTP 的话 mailhog 就会拦截信件并呈现在网页介面上。

Sail 预设的 mailhog 开在 http://localhost:8025/ 上,可以先打开看看,目前还没有任何信件。

因为这边是用 mailhog 进行测试,所以 .env 的设定只要有值就好,如果是正经的寄信
MAIL_USERNAMEMAIL_PASSWORD 就要填对应服务的帐号密码。

[email protected]
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);
    }     
}

不过现在寄信的话可以看到样式还是跟原本的信件一样。

这篇到这边有点长了,关於信件样式跟内容的改动就明天再写吧。


<<:  30天学会C语言: Day 25-抽奖的原理

>>:  Day27 [实作] 一对一视讯通话(7): 使用 Docker 封装

{DAY8} SQLite基础语法

前言 今天要开始练习SQLite基本的语法 介绍内容有 SELECT 从资料库中选取特定资料 数值...

Day 22: Recurrent Neural Network — 循环精神网路初探(上)

Recurrent Neural Network 循环精神网路 RNN是一种专门设计用以解决时间序列...

铁人赛完赛整理&开源

第12 届iT邦帮忙铁人赛系列文章 (Day30) 终於走到这一天了,每次都觉得铁人赛过程都生不如死...

[想试试看JavaScript ] HTML DOM

我们知道写程序有个阶段就是一个输入、运算处理、输出 网页是由HTML、CSS、Javascript三...

musl libc 简介与其 porting(二)Say hello to my little friend!

今天到了 day3,上次有预告了这次会开始进行RISC-V 32平台的musl libc porti...