七、邮件发送

七、邮件发送

一、开发目标

本章我们将开发以下功能:

  • 用户账号激活 —— 用于激活新注册的用户;

  • 用户密码重设 —— 帮助用户找回密码;

这两个功能都将会用到 Laravel 提供的邮件发送功能。

二、账户激活

1、账户激活

现在的登录逻辑是,用户一旦注册成功即可进行登录,本节我们要加入账号激活功能,只有当用户成功激活自己的账号时才能在网站上进行登录。为此,我们将需要为用户表新增两个字段用于保存用户的激活令牌和激活状态。激活令牌用于验证用户身份,激活状态则用于判断用户是否已激活。

整个激活流程如下:

  1. 用户注册成功后,自动生成激活令牌;

  2. 将激活令牌以链接的形式附带在注册邮件里面,并将邮件发送到用户的注册邮箱上;

  3. 用户点击注册链接跳到指定路由,路由收到激活令牌参数后映射给相关控制器动作处理;

  4. 控制器拿到激活令牌并进行验证,验证通过后对该用户进行激活,并将其激活状态设置为已激活;

  5. 用户激活成功,自动登录;

接下来让我们跟之前一样,新建一个 Git 分支来开发新功能。

$ git checkout master
$ git checkout -b account-activation-password-resets

注:有些同学在疑惑 Laravel 自带了 Email 认证 功能,为什么我们不直接使用它?本节主要有两个目的,一是学习用户激活的逻辑;二是学习怎么发送 Email。

2、资源   添加字段

在用户的账号激活功能中,我们需要为激活令牌 (activation_token) 和激活状态 (activated) 字段新增一个迁移,来将这两个字段添加到用户表中。由于我们进行的是字段添加操作,因此在命名迁移文件时需要加上前缀,遵照如 add_column_to_table 这样的命名规范,并在生成迁移文件的命令中启用 --table 项目,用于指定对应的数据库表。最终的生成命令如下:

$ php artisan make:migration add_activation_to_users_table --table=users

我们将使用随机字符来生成用户的激活令牌,因此这里的激活令牌字段需要为 string 类型,在用户成功激活以后,我们还会对激活令牌进行清空,避免用户进行多次使用,因此我们还需要将字段设置为 nullable,代表该字段允许为空。而用户的激活状态只有已激活和未激活两种状态,默认为未激活的状态,因此我们可以将激活状态设置为 boolean类型,当其值为真时,代表已激活,反之亦然。

现在让我们来为新增的迁移文件加上这两个字段。

database/migrations/[timestamp]_add_activation_to_users_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddActivivationToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('activation_token')->nullable();
            $table->boolean('activated')->default(false);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('activation_token');
            $table->dropColumn('activated');
        });
    }
}

接着我们还需要运行迁移,将字段加入到用户表中。

$ php artisan migrate

3、生成令牌

我们在前面说过,用户的激活令牌需要在用户创建(注册)之前就先生成好,这样当用户注册成功之后我们才可以将令牌附带到注册链接上,并通过邮件的形式发送给用户。

如果我们需要在模型被创建之前进行一些设置,则可以通过监听 creating 方法来做到。该方法是由 Eloquent 模型触发的一个事件。事件是 Laravel 提供一种简单的监听器实现,我们可以对事件进行监听和订阅,从而在事件被触发时接收到响应并执行一些指定操作。Eloquent 模型默认提供了多个事件,我们可以通过其提供的事件来监听到模型的创建,更新,删除,保存等操作。creating 用于监听模型被创建之前的事件,created 用于监听模型被创建之后的事件。接下来我们要生成的用户激活令牌需要在用户模型创建之前生成,因此需要监听的是 creating 方法。

在用户模型中添加 creating 方法如下。

app/Models/User.php

protected $hidden = [
    'password', 'remember_token',
];

public static function boot()
{
    parent::boot(); // TODO: Change the autogenerated stub

    static::creating(function ($user){
        $user->activation_token = str_random(30);
    });
}

boot 方法会在用户模型类完成初始化之后进行加载,因此我们对事件的监听需要放在该方法中。

现在,我们需要更新模型工厂,将生成的假用户都设为已激活状态:

database/factories/UserFactory.php

$factory->define(App\Models\User::class, function (Faker $faker) {
    $date_time = $faker->date . ' ' . $faker->time;
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'activated'=>true,
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => str_random(10),
        'created_at'=> $date_time,
        'updated_at'=> $date_time,
    ];
});

完成之后,我们重置并填充数据库。

$ php artisan migrate:refresh --seed

4、邮件程序

在接下来的功能开发中,我们将使用 Laravel 来发送邮件,为了方便测试,我们需要对环境配置文件 .env 进行配置,使用 log 邮件驱动的方式来调试邮件发送功能,这么做的好处是邮件并不会真正被发送出去,而是会出现在 storage/logs/laravel.log 文件中,该文件记录着一切 Laravel 在运行时的日志信息,有助于我们在本地进行开发调试。

.env

MAIL_DRIVER=log

5、激活路由

我们需要为用户的激活功能设定好路由,该路由将附带用户生成的激活令牌,在用户点击链接进行激活之后,我们需要将激活令牌通过路由参数传给控制器的指定动作,最终生成的激活链接例子如下:

http://weibo.test/signup/confirm/O1TTEr3faVq4fpzFXaOVQD4EAO9mQL

由上面链接我们可以推导出路由的定义应该如下。

routes/web.php

Route::get('/','StaticPagesController@home')->name('home');
Route::get('/help','StaticPagesController@help')->name('help');
Route::get('/about','StaticPagesController@about')->name('about');

Route::get('/signup','UsersController@create')->name('signup');
Route::resource('users','UsersController');
//Route::get('/users/{user}','UsersController@show')->name('users.show');

Route::get('login','SessionsController@create')->name('login');
Route::post('login','SessionsController@store')->name('login');
Route::delete('logout','SessionsController@destroy')->name('logout');

Route::get('signup/confirm/{token}','UsersController@confirmEmail')->name('confirm_email');

在 Laravel 中,我们使用视图来构建邮件模板,在用户查收邮件时,该模板将作为内容展示视图。接下来我们需要创建一个用于渲染注册邮件的 confirm 视图。

resources/views/emails/confirm.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>注册确认链接</title>
</head>
<body>
<h1>感谢您在 Weibo App 网站进行注册!</h1>

<p>
    请点击下面的链接完成注册:
    <a href="{{route('confirm_email',$user->activation_token)}}">
        {{route('confirm_email',$user->activation_token)}}
    </a>
</p>

<p>
    如果这不是您本人的操作,请忽略此邮件。
</p>
</body>
</html>

6、登录时检查是否已激活

在我们前面章节加入的登录操作中,用户即使没有激活也能够正常登录。接下来我们需要对之前的登录代码进行修改,当用户没有激活时,则视为认证失败,用户将会被重定向至首页,并显示消息提醒去引导用户查收邮件。

app/Http/Controllers/SessionsController.php

public function store(Request $request)
{
    $credentials = $this->validate($request,[
        'email'=>'required|email|max:255',
        'password'=>'required'
    ]);
    if (Auth::attempt($credentials,$request->has('remember'))){
        if (Auth::user()->activated){
            //登录成功后的相关操作
            session()->flash('success','欢迎回来!');
            $fallback = route('users.show',Auth::user());
            return redirect()->intended($fallback);
        }else{
            Auth::logout();
            session()->flash('warning','你的账号未激活,请检查邮箱中的注册邮件进行激活');
            return redirect('/');
        }

    }else{
        //登录失败后的相关操作
        session()->flash('danger','很抱歉,您的邮箱和密码不匹配');
        return redirect()->back()->withInput();
    }

    return ;
}

7、发送邮件

接下来我们要开始使用邮箱发送功能,在 Laravel 中,可以通过 Mail 接口的 send 方法来进行邮件发送,示例代码如下:

$view = 'emails.confirm';
$data = compact('user');
$from = 'summer@example.com';
$name = 'Summer';
$to = $user->email;
$subject = "感谢注册 Weibo 应用!请确认你的邮箱。";

Mail::send($view, $data, function ($message) use ($from, $name, $to, $subject) {
    $message->from($from, $name)->to($to)->subject($subject);
});

Mail 的 send 方法接收三个参数。

  • 第一个参数是包含邮件消息的视图名称。

  • 第二个参数是要传递给该视图的数据数组。

  • 最后是一个用来接收邮件消息实例的闭包回调,我们可以在该回调中自定义邮件消息的发送者、接收者、邮件主题等信息。

接下来让我们为用户控制器定义一个 sendEmailConfirmationTo 方法,该方法将用于发送邮件给指定用户。我们会在用户注册成功之后调用该方法来发送激活邮件,具体代码实现如下(请注意顶部加载 use Mail;):

app/Http/Controllers/UsersController.php

public function store(Request $request)
{
    $this->validate($request,[
        'name' => 'required|max:50',
        'email' => 'required|email|unique:users|max:255',
        'password'=>'required|confirmed|min:6'
    ]);
    $user = User::create([
        'name'=>$request->name,
        'email'=>$request->email,
        'password'=>bcrypt($request->password),
    ]);
    $this->sendEmailConfirmationTo($user);
    session()->flash('success','验证邮件已发送到你的注册邮箱上,请注意查收。');
    return redirect('/');
}

protected function sendEmailConfirmationTo($user)
{
    $view = 'emails.confirm';
    $data = compact('user');
    $from = 'summer@example.com';
    $name = 'Summer';
    $to = $user->email;
    $subject = "感谢注册 Weibo 应用!请确认你的邮箱";

    Mail::send($view,$data,function ($message) use ($from,$name,$to,$subject){
        $message->from($from,$name)->to($to)->subject($subject);
    });
}

请注意我们需要调用 use Mail 来引入邮件相关的操作方法。通过上面代码可以看到,我们把之前用户注册成功之后进行的登录操作:

Auth::login($user);

替换为了激活邮箱的发送操作:

$this->sendEmailConfirmationTo($user);

注册成功提示语改为查看邮箱的提示语。在激活邮件发送成功之后,我们还会将用户重定向至首页,而并非之前的用户个人页。

8、激活功能

现在的邮箱发送功能已经能够正常使用,接下来让我们完成前面定义的 confirm_email 路由对应的控制器方法 confirmEmail,来完成用户的激活操作。并且在 __construct 方法里开启未登录用户访问权限。

app/Http/Controllers/UsersController.php

public function __construct()
{
    $this->middleware('auth',[
        'except' => ['show','create','store','index','confirmEmail']
    ]);

    $this->middleware('guest',[
        'only' => ['create']
    ]);
}

public function confirmEmail($token)
{
    $user = User::where('activation_token',$token)->firstOrFail();

    $user->activated = true;
    $user->activation_token = null;
    $user->save();

    Auth::login($user);
    session()->flash('success','恭喜你,激活成功');
    return redirect()->route('users.show',[$user]);

}

Auth 中间件黑名单中,我们增加了 confirmEmail 来开启未登录用户的访问。

在 confirmEmail 中,我们会先根据路由传送过来的 activation_token 参数从数据库中查找相对应的用户,Eloquent 的 where 方法接收两个参数,第一个参数为要进行查找的字段名称,第二个参数为对应的值,查询结果返回的是一个数组,因此我们需要使用 firstOrFail 方法来取出第一个用户,在查询不到指定用户时将返回一个 404 响应。在查询到用户信息后,我们会将该用户的激活状态改为 true,激活令牌设置为空。最后将激活成功的用户进行登录,并在页面上显示消息提示和重定向到个人页面。

如果我们尝试注册一个新用户:

image.png



现在打开 Laravel 的 Log 文件,默认是按照日期切割的,如:laravel-2018-12-13.log ,尝试在 laravel-{today}.log文件的最后面,找找看类似以下的内容:

storage/logs/laravel-{today}.log

image.png


上面的邮件主题(Subject)由于 UTF-8 解码原因导致文字没有正常显示出来,不过没关系,通过 Log 我们已经可以判定邮件已经可以成功发送。在浏览器上尝试访问在 Log 文件中输出的激活链接,则可以看到新注册用户被成功激活:

image.png


Git 代码版本控制

接着让我们将本次更改纳入版本控制中:

$ git add -A
$ git commit -m "用户激活"

三、密码重设

1、密码重设


上一节我们完成了账户激活功能,接着让我们继续开发密码重设功能。由于很多常用的 Web 应用都会提供密码重设的功能,因此 Laravel 将此功能内置到了框架中,减少了很多开发成本。

不过即便我们要使用 Laravel 内置的重设密码功能,也需要知道在日常开发时,大多数 Web 工程师是如何为网站添加重设密码功能的。一般来说,密码重设的步骤如下:

  1. 用户点击重设密码链接并跳转到重设密码页面;

  2. 在重设密码页面输入邮箱信息并提交;

  3. 控制器通过该邮箱查找到指定用户并为该用户生成一个密码令牌,接着将该令牌以链接的形式发送到用户提交的邮箱上;

  4. 用户查看自己个人邮箱,点击重置密码链接跳转到重置密码页面;

  5. 用户在该页面输入自己的邮箱和密码并提交;

  6. 控制器对用户的邮箱和密码重置令牌进行匹配,匹配成功则更新用户密码;

了解其中的整个流程之后,接下来的开发就容易多了。

2、资源

在密码重设功能中,我们还会用到一个用来保存密码重置令牌的数据表,Laravel 已默认为我们生成好了该数据表。

database/migrations/2014_10_12_100000_create_password_resets_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePasswordResetsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('password_resets', function (Blueprint $table) {
            $table->string('email')->index();
            $table->string('token');
            $table->timestamp('created_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('password_resets');
    }
}

可以看到 Laravel 默认生成的密码重置表有三个字段 email, token, created_at,分别用于生成用户邮箱、密码重置令牌、密码重置令牌的创建时间,并为邮箱和密码重置令牌加上了索引,这样在数据库使用这两个字段进行查找时效率更快。由于我们在前面已经执行过了迁移,因此现在我们的数据库已经拥有了 password_resets 表,无需再次运行迁移生成。

Laravel 将密码重设功能相关的逻辑代码都放在了 ForgotPasswordController 和 ResetPasswordController 中,因此我们接下来需要将重设密码相关的路由指定到该控制器上。

routes/web.php

Route::get('password/reset','Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email','Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}','Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset','Auth\ResetPasswordController@reset')->name('password.update');

该路由列表的信息如下所示:

HTTP 请求URL动作作用
GET/password/resetAuth\ForgotPasswordController
@showLinkRequestForm
显示重置密码的邮箱发送页面
POST/password/emailAuth\ForgotPasswordController
@sendResetLinkEmail
邮箱发送重设链接
GET/password/reset/{token}Auth\ResetPasswordController
@showResetForm
密码更新页面
POST/password/resetAuth\ResetPasswordController
@reset
执行密码更新操作

现在路由已配置完成,由于 Laravel 在 ForgotPasswordController 和 ResetPasswordController 中已经为我们处理好一些相关逻辑,因此我们现在只需要将精力专注于视图的构建就行了。

接下来让我们修改用户的登录页面,添加修改密码的链接,方便忘记密码的用户在第一时间找到入口。

resources/views/sessions/create.blade.php

@extends('layouts.default')
@section('title','登录')

@section('content')
    <div class="offset-md-2 col-md-8">
        <div class="card ">
            <div class="card-header">
                <h5>登录</h5>
            </div>
            <div class="card-body">
                @include('shared._errors')

                <form method="POST" action="{{ route('login') }}">
                    {{ csrf_field() }}

                    <div class="form-group">
                        <label for="email">邮箱:</label>
                        <input type="text" name="email" class="form-control" value="{{ old('email') }}">
                    </div>

                    <div class="form-group">
                        <label for="password">密码:</label>
                        <input type="password" name="password" class="form-control" value="{{ old('password') }}">
                    </div>

                    <div class="form-group">
                        <label for="password">密码(<a href="{{route('password.request')}}">忘记密码</a>):</label>
                        <input type="password" name="password" class="form-control" value="{{ old('password') }}">
                    </div>

                    <div class="form-group">
                        <div class="form-check">
                            <input type="checkbox" class="form-check-input" name="remember" id="exampleCheck1">
                            <label class="form-check-label" for="exampleCheck1">记住我</label>
                        </div>
                    </div>

                    <button type="submit" class="btn btn-primary">登录</button>
                </form>

                <hr>

                <p>还没账号?<a href="{{ route('signup') }}">现在注册!</a></p>
            </div>
        </div>
    </div>
@stop

image.png

3、充值密码表单

Laravel 生成的密码控制器在 getEmail 动作中返回的视图为 auth.passwords.email,因此让我们新建该视图,并在里面加上用户重置密码的表单。

resources/views/auth/passwords/email.blade.php

@extends('layouts.default')
@section('title','重置密码')

@section('content')
    <div class="col-md-8 offset-md-2">
        <div class="card ">
            <div class="card-header"><h5>重置密码</h5></div>

            <div class="card-body">
                @if (session('status'))
                    <div class="alert alert-success">
                        {{ session('status') }}
                    </div>
                @endif

                <form class="" method="POST" action="{{ route('password.email') }}">
                    {{ csrf_field() }}

                    <div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
                        <label for="email" class="form-control-label">邮箱地址:</label>

                        <input id="email" type="email" class="form-control" name="email" value="{{ old('email') }}" required>

                        @if ($errors->has('email'))
                            <span class="form-text">
              <strong>{{ $errors->first('email') }}</strong>
            </span>
                        @endif
                    </div>

                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">
                            发送密码重置邮件
                        </button>
                    </div>
                </form>
            </div>
        </div>
    </div>
@stop


image.png

填入注册的用户 Email 来找回密码:

image.png

提交后:

image.png

4、填写新密码

提交成功后,我们可以在 storage/logs/laravel-{today}.log 里找到重置链接发送的邮件 Log,类似以下:

image.png

复制上面的链接,然后使用浏览器访问:

image.png


视图 auth.passwords.reset 没找到,接下来我们来创建此视图:

resources/views/auth/passwords/reset.blade.php

@extends('layouts.default')
@section('title','更新密码')

@section('content')
    <div class="offset-md-1 col-md-10">
        <div class="card">
            <div class="card-header">
                <h5>更新密码</h5>
            </div>

            <div class="card-body">
                <form method="POST" action="{{ route('password.update') }}">
                    @csrf

                    <input type="hidden" name="token" value="{{ $token }}">

                    <div class="form-group row">
                        <label for="email" class="col-md-4 col-form-label text-md-right">Email 地址</label>

                        <div class="col-md-6">
                            <input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" required autofocus>

                            @if ($errors->has('email'))
                                <span class="invalid-feedback" role="alert">
              <strong>{{ $errors->first('email') }}</strong>
            </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="password" class="col-md-4 col-form-label text-md-right">密码</label>

                        <div class="col-md-6">
                            <input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" required>

                            @if ($errors->has('password'))
                                <span class="invalid-feedback" role="alert">
              <strong>{{ $errors->first('password') }}</strong>
            </span>
                            @endif
                        </div>
                    </div>

                    <div class="form-group row">
                        <label for="password-confirm" class="col-md-4 col-form-label text-md-right">确认密码</label>

                        <div class="col-md-6">
                            <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required>
                        </div>
                    </div>

                    <div class="form-group row mb-0">
                        <div class="col-md-6 offset-md-4">
                            <button type="submit" class="btn btn-primary">
                                重置密码
                            </button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
@stop

我们在用户进行表单提交时,会将密码重置的令牌信息通过隐藏输入框一同提交给密码控制器的 getReset 进行处理。

<input type="hidden" name="token" value="{{ $token }}">

image.png



另外还有一点需要我们注意的是,Laravel 在 ResetPasswordController 中默认为我们设置好了密码重置成功后的重定向地址 /home,而由于我们在本项目中并没有为 /home 绑定任何控制器动作,因此我们需要对 ResetPasswordController 的 $redirectTo 属性进行修改,将默认重定向地址改为 /,改动后的 ResetPasswordController 如下:

app/Http/Controllers/Auth/ResetPasswordController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;

class ResetPasswordController extends Controller
{

    use ResetsPasswords;

    protected $redirectTo = '/';

    public function __construct()
    {
        $this->middleware('guest');
    }
}

控制器的业务代码皆被封装到 ResetsPasswords trait 里,完整路径如下,推荐你打开此文件进行简单阅读,以知晓重置功能的底层逻辑:

vendor/laravel/framework/src/Illuminate/Foundation/Auth/ResetsPasswords.php

例如上面视图 auth.passwords.reset 加载报错的逻辑,就是方法 showResetForm() 里对此 View 进行了加载:

image.png


输入邮箱和新密码来测试重置的最后流程:

image.png


至此,整个用户密码重设功能便完成了,由此我们可以看到 Laravel 开发的高效。

Git 代码版本控制

接着让我们将本次更改纳入版本控制中:

$ git add -A
$ git commit -m "重置密码"

四、在生产环境中发送邮件

1,邮件通知

本章节我们将为『话题有新回复』通知新增『邮件通知频道』,当话题被回复时,作者可以收到一份 Email 邮件通知。Laravel 的通知系统默认支持邮件频道的通知方式,我们只需要稍作配置即可。

Laravel 支持多种邮件驱动,包括 smtp、Mailgun、Maildrill、Amazon SES、mail 和 sendmail。Mailgun 、 Amazon SES 、Maildrill 都是第三方邮件服务(Laravel 文档 )。mail 驱动使用 PHP 提供的 mail 函数。sendmail 驱动通过 Sendmail/Postfix(Linux)提供的命令发送邮件,smtp 驱动使用支持 ESMTP 的 SMTP 服务器发送邮件。mail 不安全,sendmail 需要安装配置 Sendmail/Postfix,并且信用不高,很容易被当成垃圾邮件,第三方服务的信用是最高的,商业软件推荐使用。

本章节以 QQ 邮箱为例,我们将开启 QQ 的 SMTP 功能,并配置项目的 SMTP 邮件发送功能。其他邮箱的配置基本大致相同。

2、开启QQ邮箱的SMTP支持

image.png


复制方框里的『授权码』,授权码将作为我们的密码使用:(已开启忘记授权码的关闭重新开启一下)

image.png

3、邮箱发送配置

Laravel 中邮箱发送的配置存放于 config/mail.php 中。不过 mail.php 中我们所需的配置,都可以通过 .env 来配置。作为最佳实践,我们优先选择通过环境变量来配置:

.env

MAIL_DRIVER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=25
MAIL_USERNAME=xxxxxxxxxxxxxx@qq.com
MAIL_PASSWORD=xxxxxxxxx
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=xxxxxxxxxxxxxx@qq.com
MAIL_FROM_NAME=WeiboApp

选项讲解:

  1. MAIL_DRIVER=smtp —— 使用支持 ESMTP 的 SMTP 服务器发送邮件;

  2. MAIL_HOST=smtp.qq.com —— QQ 邮箱的 SMTP 服务器地址,必须为此值;

  3. MAIL_PORT=25 —— QQ 邮箱的 SMTP 服务器端口,必须为此值;

  4. MAIL_USERNAME=xxxxxxxxxxxxxx@qq.com —— 请将此值换为你的 QQ + @qq.com

  5. MAIL_PASSWORD=xxxxxxxxx —— 密码是我们第一步拿到的授权码;

  6. MAIL_ENCRYPTION=tls —— 加密类型,选项 null 表示不使用任何加密,其他选项还有 ssl,这里我们使用 tls 即可。

  7. MAIL_FROM_ADDRESS=xxxxxxxxxxxxxx@qq.com —— 此值必须同 MAIL_USERNAME 一致;

  8. MAIL_FROM_NAME=WeiboApp —— 用来作为邮件的发送者名称。

接着我们要对 Heroku 应用进行配置,参照上 .env 里的信息:

$ heroku config:set MAIL_DRIVER=smtp
$ heroku config:set MAIL_HOST=smtp.qq.com
$ heroku config:set MAIL_PORT=25
$ heroku config:set MAIL_USERNAME=xxxxxxxxxxxxxx@qq.com
$ heroku config:set MAIL_PASSWORD=xxxxxxxxx
$ heroku config:set MAIL_ENCRYPTION=tls
$ heroku config:set MAIL_FROM_ADDRESS=xxxxxxxxxxxxxx@qq.com
$ heroku config:set MAIL_FROM_NAME=WeiboApp

4、历史遗留问题

还记得 账户激活 一节的邮件发送逻辑吗?现在我们已经在环境配置文件完善了邮件的发送配置,因此不再需要使用 from 方法:

app/Http/Controllers/UsersController.php

protected function sendEmailConfirmationTo($user)
    {
        $view = 'emails.confirm';
        $data = compact('user');
//        $from = 'summer@example.com';
//        $name = 'Summer';
        $to = $user->email;
        $subject = "感谢注册 Weibo 应用!请确认你的邮箱";

        Mail::send($view,$data,function ($message) use (/*$from,$name,*/$to,$subject){
            $message->/*from($from,$name)->*/to($to)->subject($subject);
        });
    }

一切完成之后,让我们将更改的代码进行提交,并切回到主分支上进行合并。

$ git add -A
$ git commit -m "移除邮件发送的 from 选项"
$ git checkout master
$ git merge account-activation-password-resets

接着再将代码推送到 GitHub 和 Heroku 上。

$ git push
$ git push heroku master

由于我们在本章节中为账号激活功能新增了字段,因此需要在 Heroku 上运行迁移。

$ heroku run php artisan migrate

现在,邮件发送功能已经可以在线上正式使用了。

如果你之前有在线上环境注册过一批用户,则可以使用以下命令连接线上的 Tinker 环境:

$ heroku run php artisan tinker

并对所有已注册过的用户进行批量激活的操作:

>>> App\Models\User::where('activated', false)->update(['activated' => true, 'activation_token' => null])

至此,所有之前在线上已注册过的用户已被成功激活。

五、小结

经过本章节的学习,我们学到了以下内容:

  • 使用迁移为数据表新增字段;

  • 通过事件监听来做一些操作;

  • 在 Laravel 应用使用邮件发送功能;

  • 在本地调试邮件发送功能;

  • 通过邮件发送注册链接来激活用户;

  • 通过注册邮件来找回密码;
















回复列表



回复操作

正在加载验证码......

请先拖动验证码到相应位置