2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Laravel】送信したメールの開封検知をする

Posted at

Laravelから送信したメールが既読されたかどうか、開封検知機能を実装します。

注意点

メールを受け取った側のメーラーアプリがHTMLメールを受信できることが前提です。逆に言えばHTMLを受け取れない場合、自動でテキストメールに変換してしまう場合等は開封検知ができません。

また、この方法では開封されたかどうか正確に検知することができません。実際には開封していないのに「開封した」と判断するパターンや、開封したのに検知することができないパターンなどがあります。

仕様

「1x1の透明な画像をHTMLメールに埋め込む」ことで開封検知をします。
1x1画像用のルーティングを用意し、そのURLにアクセスがあった場合にDBに記録する、という形とします。

動作環境

・PHP 7.4.11
・MySQL 8.0.21
・Laravel Framework 8.10.0

全てローカルで動作させます

実装

.envファイルの修正

メールのドライバはsmtpでmailtrapを使用します。

.env
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
xxxx_xx_xx_xxxxxx_create_logs_table.php
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アドレスをカラムとしてもたせます
Log.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Log extends Model
{
    use HasFactory;

    protected $guarded = ['id'];
}

開封検知用ルーティングを定義

web.php
use App\Http\Controllers\OpenLogController;

Route::get('images', [OpenLogController::class, 'openMail'])->name('open_mail');

開封検知のコントローラー作成

$ php artisan make:controlle OpenLogController
Controller created successfully.
OpenLogController.php
<?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さんこちらの記事を参考にさせていただきました。

メール送信用のルーティングを追記

web.php
Route::get('send', function () {
    Mail::send('mails.hello', function ($message) {
        $message->to('john@johndoe.com', 'John Doe');
        $message->subject('Subject');
    });

    return "メール送信完了";
});

メールテンプレートの作成

resources/views/mails/hello.php
<h1>テストメール</h1>
<img src="{{ route('open_mail')}}" alt="">
  • imgタグのsrc属性に開封検知用のルーティングを設定。開封時にログを取得、その後に1x1の透明なpngを返却しています

テスト

php artisan serveでビルトインサーバーを起動してlocalhost:8000/sendにアクセスすると、メール送信に数秒秒かかったのちに「メール送信完了」と表示されます。

mailtrap側に届いているメールを開くとDBにLogが保存されているはずです。

以上、HappyなLaravelライフを🎉

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?