Laravelから送信したメールが既読されたかどうか、開封検知機能を実装します。
注意点
メールを受け取った側のメーラーアプリがHTMLメールを受信できることが前提です。逆に言えばHTMLを受け取れない場合、自動でテキストメールに変換してしまう場合等は開封検知ができません。
また、この方法では開封されたかどうか正確に検知することができません。実際には開封していないのに「開封した」と判断するパターンや、開封したのに検知することができないパターンなどがあります。
仕様
「1x1の透明な画像をHTMLメールに埋め込む」ことで開封検知をします。
1x1画像用のルーティングを用意し、そのURLにアクセスがあった場合にDBに記録する、という形とします。
動作環境
・PHP 7.4.11
・MySQL 8.0.21
・Laravel Framework 8.10.0
全てローカルで動作させます
実装
.envファイルの修正
メールのドライバはsmtpでmailtrapを使用します。
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=[mailtrapで指定された値]
MAIL_PASSWORD=[mailtrapで指定された値]
MAIL_ENCRYPTION=tls
ログ保存用のモデル作成
$ php artisan make:model Log -m
Model created successfully.
Created Migration: xxxx_xx_xx_xxxxxx_create_logs_table
class CreateLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('logs', function (Blueprint $table) {
$table->id();
$table->string('user_agent');
$table->string('ip_address');
$table->timestamps();
});
}
~~省略~~
- ログ保存用のモデル「Log」はユーザーエージェントとIPアドレスをカラムとしてもたせます
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Log extends Model
{
use HasFactory;
protected $guarded = ['id'];
}
開封検知用ルーティングを定義
use App\Http\Controllers\OpenLogController;
Route::get('images', [OpenLogController::class, 'openMail'])->name('open_mail');
開封検知のコントローラー作成
$ php artisan make:controlle OpenLogController
Controller created successfully.
<?php
namespace App\Http\Controllers;
use App\Models\Log;
use Illuminate\Http\Request;
class OpenLogController extends Controller
{
public function openMail(Request $request)
{
// リクエストからUAとIPアドレスを取得して保存
Log::create([
'user_agent' => $request->userAgent(),
'ip_address' => $request->ip(),
]);
// 透明な1x1のpng画像をクライアントへ返却
return response(base64_decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQI12NgYAAAAAMAASDVlMcAAAAASUVORK5CYII="))
->header('Content-type', 'image/png');
}
}
透明な1x1のpng画像の点はnanowebさんのこちらの記事を参考にさせていただきました。
メール送信用のルーティングを追記
Route::get('send', function () {
Mail::send('mails.hello', function ($message) {
$message->to('john@johndoe.com', 'John Doe');
$message->subject('Subject');
});
return "メール送信完了";
});
メールテンプレートの作成
<h1>テストメール</h1>
<img src="{{ route('open_mail')}}" alt="">
- imgタグのsrc属性に開封検知用のルーティングを設定。開封時にログを取得、その後に1x1の透明なpngを返却しています
テスト
php artisan serve
でビルトインサーバーを起動してlocalhost:8000/sendにアクセスすると、メール送信に数秒秒かかったのちに「メール送信完了」と表示されます。
mailtrap側に届いているメールを開くとDBにLogが保存されているはずです。
以上、HappyなLaravelライフを🎉