发送邮件(运用Laravel的邮件、队列、任务调度、Redis等服务)

本文总结运用Laravel的邮件、队列、任务调度、Redis等服务来完成定时给新注册的邮件用户发送邮件的功能(附上编写适配性好的邮件页面的方法)。

我们通过制定计划任务来定时执行自定的Artisan命令,在命令中去执行发送邮件的业务逻辑代码(我们将发送邮件的方法写在队列文件中来调用)。
共分为五步来实现:
第一步:配置邮件服务
第二步:配置队列服务
第三步:配置计划任务,自定义Artisan命令
第四步:编写业务代码
第五步:调试邮件适配


第一步:配置邮件服务

Laravel服务-邮件说明文档
实际应用:

简介
Laravel基于目前流行的SwiftMailer库提供了一套干净清爽的邮件API。Laravel为SMTP、Mailgun、Mandrill、Amazon SES、PHP的mail函数,以及sendmail提供了驱动,从而允许你快速通过本地或云服务发送邮件。
基于驱动的API如Mailgun和Mandrill通常比SMTP服务器更简单、更快。所有的API驱动要求应用已经安装Guzzle HTTP库。你可以通过添加如下行到composer.json文件来安装Guzzle到项目:

"guzzlehttp/guzzle": "~5.3|~6.0"

我们这里使用smtp,.env文件配置如下:

MAIL_DRIVER=smtp
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=

发送邮件
Laravel允许你在视图中存储邮件信息,例如,要组织你的电子邮件,可以在resources/views目录下创建emails目录。

要发送一条信息,使用Mail门面上的send方法。send方法接收三个参数。第一个参数是包含邮件信息的视图名称;第二个参数是你想要传递到该视图的数组数据;第三个参数是接收消息实例的闭包回调——允许你自定义收件人、主题以及邮件其他方面的信息,我们在app/Jobs/SendMail.php队列中写发送邮件代码:
(配置队列见下一步)


namespace App\Jobs;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;

class SendMail extends Job implements SelfHandling, ShouldQueue
{
    use SerializesModels;
    protected $username;
    protected $email;
    protected $subject;
    protected $viewTemplate;
    protected $data;
    protected $fromAddress;
    protected $fromName;
    public function __construct($username, $email, $subject, $viewTemplate, $data = [], $fromAddress = null, $fromName = null)
    {
        $this->username = $username;
        $this->email = $email;
        $this->subject = $subject;
        $this->viewTemplate = $viewTemplate;
        $this->data = $data;
        $this->fromAddress = $fromAddress;
        $this->fromName = $fromName;
    }
    public function handle()
    {
        // 首先在handle的时候,会让job尝试3次,失败后被扔回queue,再重置执行
        for ($i = 0; $i < 3; ++$i) {
            if ($this->sendMail()) {
                return true;
            }
        }
        return true;
        //throw new \Exception('send mail failed');
    }
    protected function sendMail()
    {
        $username = $this->username;
        $email = $this->email;
        $subject = $this->subject;
        $fromAddress = is_null($this->fromAddress) ? env('MAIL_FROM_ADDRESS') : $this->fromAddress;
        $fromName = is_null($this->fromName) ? env('MAIL_FROM_NAME') : $this->fromName;
        $status = Mail::send($this->viewTemplate, $this->data, function ($message) use ($email, $username, $subject,
            $fromAddress, $fromName) {
            $message->from($fromAddress, $fromName)->to($email, $username)->subject($subject);
        });
        return $status;
    }
}

构造消息
传递给send方法的第三个参数是一个允许你指定邮件消息本身多个选项的闭包。使用这个闭包可以指定消息的其他属性,例如抄送、群发,等等:

$message->from($address, $name = null);
$message->sender($address, $name = null);
$message->to($address, $name = null);
$message->cc($address, $name = null);
$message->bcc($address, $name = null);
$message->replyTo($address, $name = null);
$message->subject($subject);
$message->priority($level);
$message->attach($pathToFile, array $options = []);
// 从$data字符串追加文件...
$message->attachData($data, $name, array $options = []);
// 获取底层SwiftMailer消息实例...
$message->getSwiftMessage();

第二步:配置队列服务

Laravel服务-队列说明文档
实际应用:

简介
Laravel队列服务为各种不同的后台队列提供了统一的API。队列允许你推迟耗时任务(例如发送邮件)的执行,从而大幅提高web请求速度。
配置
队列配置文件存放在config/queue.php。在该文件中你将会找到框架自带的每一个队列驱动的连接配置,包括数据库、Beanstalkd、 IronMQ、 Amazon SQS、 Redis以及同步(本地使用)驱动。其中还包含了一个null队列驱动以拒绝队列任务。
队列依赖
* database:为了使用database队列驱动,需要一张数据库表来存放任务,要生成创建该表的迁移,运行Artisan命令queue:table,迁移被创建好了之后,使用migrate命令运行迁移:

php artisan queue:table
php artisan migrate
  • Amazon SQS: aws/aws-sdk-php ~3.0
  • Beanstalkd: pda/pheanstalk ~3.0
  • IronMQ: iron-io/iron_mq ~2.0
  • Redis: predis/predis ~1.0

我们这里使用同步,在.env中配置QUEUE_DRIVER=sync

编写任务类
1)生成任务类
默认情况下,应用的所有队列任务都存放在app/Jobs目录。你可以使用Artisan CLI生成新的队列任务:

php artisan make:job SendMail --queued

该命令将会在app/Jobs目录下生成一个新的类,并且该类实现了Illuminate\Contracts\Queue\ShouldQueue接口,告诉Laravel该任务应该被推送到队列而不是同步运行。
2) 任务类结构
任务类非常简单,正常情况下只包含一个当队列处理该任务时被执行的handle方法,我们将发送邮件直接写在handle里,实例代码见上一步中的发送邮件部分。

handle方法在任务被队列处理的时候被调用,注意我们可以在任务的handle方法中对依赖进行类型提示。Laravel服务容器会自动注入这些依赖。

推送任务到队列
默认的Laravel控制器位于app/Http/Controllers/Controller.php并使用了DispatchesJobs trait
该trait提供了一些允许你方便推送任务到队列的方法,例如dispatch方法(在控制器中):



namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller{
    /**
     * 发送提醒邮件到指定用户
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);

        $this->dispatch(new SendReminderEmail($user));
    }
}

当然,有时候你想要从应用中路由或控制器之外的某些地方分发任务,因为这个原因,你可以在应用的任何类中包含DispatchesJobs trait,从而获取对分发方法的访问,举个例子,下面是使用该trait的示例类:



namespace App;

use Illuminate\Foundation\Bus\DispatchesJobs;

class ExampleClass{
    use DispatchesJobs;
}

为任务指定队列

你还可以指定任务被发送到的队列。

通过推送任务到不同队列,你可以对队列任务进行“分类”,甚至优先考虑分配给多个队列的worker数目。这并不会如队列配置文件中定义的那样将任务推送到不同队列“连接”,而只是在单个连接中发送给特定队列。要指定该队列,使用任务实例上的onQueue方法,该方法有Laravel自带的基类App\Jobs\Job提供



namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use App\Jobs\SendReminderEmail;
use App\Http\Controllers\Controller;

class UserController extends Controller{
    /**
     * 发送提醒邮件到指定用户
     *
     * @param  Request  $request
     * @param  int  $id
     * @return Response
     */
    public function sendReminderEmail(Request $request, $id)
    {
        $user = User::findOrFail($id);
        $job = (new SendReminderEmail($user))->onQueue('emails');
        $this->dispatch($job);
    }
}

实例代码见第四步


第三步:配置计划任务,自定义Artisan命令

我们通过设置计划任务来定时调用自定义的Artisan命令,命令里执行发送邮件的逻辑,来实现定时发送邮件。

Laravel服务-任务调度说明文档
Laravel自定义Artisan命令说明文档
实际应用:

任务调度简介
在以前,开发者需要为每一个需要调度的任务编写一个Cron条目,这是很让人头疼的事。你的任务调度不在源码控制中,你必须使用SSH登录到服务器然后添加这些Cron条目。Laravel命令调度器允许你平滑而又富有表现力地在Laravel中定义命令调度,并且服务器上只需要一个Cron条目即可。
任务调度定义在app/Console/Kernel.php文件的schedule方法中,该方法中已经包含了一个示例。你可以自由地添加你需要的调度任务到Schedule对象。

1)在服务器上开启调度
在服务的/var/spool/cron/root文件中添加代码,
在命令行输入:crontab -e
添加如下代码:* * * * * php路径 项目路径/artisan schedule:run >> /dev/null 2>&1
在命令行输入:crontab -u root -l 查看

不需要重启cron服务,因为系统每分钟都会读一遍/var/spool/cron目录下的文件

如果发现按照如下配置还是不能执行的话,可以用以下方法排除问题:
看一下命令有没有使用绝对路径,比如这里使用/usr/local/php/bin/php而不是php,使用/data/wwwroot/test/artisan而不是artisan。
如果已经使用了绝对路径还是不执行,那就直接在命令行输入/usr/local/php/bin/php /data/wwwroot/test/artisan schedule:run 1>> /dev/null 2>&1,看看有没有执行,如果没有执行,那就是laravel代码的问题,如果执行了说明是环境变量的问题,好好检查路径的问题。如果不知道php在什么地方,在命令行输入which php,就会提示你php安装在什么位置了。

2)定义调度
可以在App\Console\Kernel类的schedule方法中定义所有调度任务。

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        \App\Console\Commands\ICO\SendEmail::class,
    ];

    /**
     * Define the application's command schedule.
     *
     * @param \Illuminate\Console\Scheduling\Schedule $schedule
     */
    protected function schedule(Schedule $schedule)
    {
        // 5 minutes every
        $schedule->command('icoemails:send')->everyFiveMinutes();
    }
}

还可以调度闭包调用、Artisan命令和操作系统命令
自定义Artisan命令

php artisan make:console App/Console/Commands/ICO/SendEmail --command=icoemails:send

第四步:编写业务代码

如上步,我们的业务逻辑代码通过执行命令去执行,handle方法如下:
这里只写出发送邮件的代码:

$job = (new SendMail('', $e['email'], $title, $emailTempFile, $data))->onQueue('push_notify_' . rand(0, 4));
$res = app('Illuminate\Contracts\Bus\Dispatcher')->dispatch($job);

第五步:调试邮件适配

编写邮件模版的时候我们会发现有很多限制,这里有一个不错的总结:
从头开始构建一个HTML电子邮件模板

–感谢laravel学院对广大laravel爱好者的帮助

你可能感兴趣的:(PHP,Laravel)