メール, PHP

Laravel SendGridライブラリを使う為のUtil作ったった

SendGrid

SendGrid謹製のライブラリインストール

 

$ composer require sendgrid/sendgrid

サンプルコード

ドキュメントと、ここのサンプルコードを参考にしながら作っていくと良い🐱

 

personalized処理

PHPでSendGridを使ってメールを一斉送信する方法

 

 

Util作ったった

 

<?php

namespace App\Services\Mail;

use App\Util\SendGrid\SendGrid;

class MailService
{
    public function __construct(
        SendGrid $util_sendgird
    ) {
        $this->util_sendgrid = $util_sendgrid;
    }

    /**
     * SendGridによるメール配信
     *
     * @param array $data
     * @return void
     */
    public function sendMailsBySendGrid(array $data): void
    {
        $this->util_sendgrid->setMailData($data);
        $this->util_sendgrid->sendMails();
    }
}

 

.env

SENDGRID_API_KEY=xxxxxx

 

$ php artisan config:clear

 

<?php

namespace App\Util\SendGrid;

use App\Exceptions\SendGridApiException;
use Dotenv\Exception\ValidationException;
use Illuminate\Support\Carbon;
use SendGrid\Mail\To;

class SendGrid
{
    private $_mail_data = [];
    private $_validator;
    private $_sendgrid_email;
    private $_chunked_emails_array = [];

    const SENDGRID_SEND_ONCE_REQUEST_LIMIT = 1000;

    public function __construct()
    {
        $this->validator = app()->make(\Illuminate\Validation\Factory::class);
        $this->sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));
    }

    /**
     * 最初にデータをセット
     *
     * @param array $data
     * @param bool $is_test_flag
     * @return void
     */
    public function setMailData(array $data, ?bool $is_test_flag = false): void
    {
        $this->_testData($data, $is_test_flag);
        $this->_mail_data = $data;

        // 1,000件単位で1,000メールアドレス/1リクエストに分ける為に1,000件で1配列ずつに分割
        $this->_chunked_emails_array = array_chunk($this->_mail_data['emails'], self::SENDGRID_SEND_ONCE_REQUEST_LIMIT);
    }

    /**
     * バリデーション
     *
     * @param array $data
     * @param bool $is_test_flag
     * @return void
     */
    private function _testData(array $data, bool $is_test_flag): void
    {
        $rules = [
            // 一斉送信 送信宛先メールアドレス
            'emails' => [
                'required',
                'array',
            ],
            // 差出元アドレス
            'from_address' => [
                'required',
                'string',
            ],
            // 差出人名
            'from_name' => [
                'required',
                'string',
            ],
            // 件名
            'subject' => [
                'required',
                'string',
            ],
            // 本文
            'mail_body' => [
                'required',
                'string',
            ],
            // SendGrid側の配信リクエストid
            'category' => [
                'required',
                'string',
            ],
        ];
        // 予約リクエストid
        if (isset($data['batch_id']) && !empty($data['batch_id'])) {
            $rules['batch_id'] = [
                'string',
            ];
        }
        // 予約配信 指定時刻
        if (isset($data['delivery_datetime']) && !empty($data['delivery_datetime'])) {
            $rules['delivery_datetime'] = [
                'date_format:Y-m-d H:i:s',
            ];
        }
        // テスト配信の場合 統計情報に含めないのでcategoryをチェックしない
        if ($is_test_flag) {
            unset($rules['category']);
        }

        try {
            $this->_validator = $this->validator->make($data, $rules);
            $this->_validator->validate();
        } catch (ValidationException $e) {
            \Log::error('Caught Exception: ' . $e->getMessage());
            throw $e;
        } catch (\Throwable $t) {
            \Log::error('Caught Throwable: ' . $t->getMessage());
            throw $t;
        }
    }

    /**
     * SendGridへメール送信リクエスト実行
     *
     * @return void
     *
     * @see 個人情報保護処理 https://sendgrid.kke.co.jp/blog/?p=12490
     */
    public function sendMails(): void
    {
        try {
            foreach ($this->_chunked_emails_array as $emails) {
                // 1リクエスト単位でインスタンス生成
                $this->_sendgrid_email = app()->make(\SendGrid\Mail\Mail::class);
                // 個人情報保護
                foreach ($emails as $email) {
                    $personalization = app()->make(\SendGrid\Mail\Personalization::class);
                    $personalization->addTo(new To($email));
                    $this->_sendgrid_email->addPersonalization($personalization);
                }
                $this->_setHeader();
                // SendGridにリクエスト送信
                $response = $this->sendgrid->send($this->_sendgrid_email);
                $body = json_decode($response->body(), true);
                // 2xxでなかったらエラー
                if (strpos($response->statusCode(), "2") !== 0) {
                    throw $this->_getSendGridApiException($body);
                }
            }
        } catch (SendGridApiException $t) {
            \Log::error('Caught SendGridApiException: ' . $response->body());
            throw $t;
        } catch (\Throwable $t) {
            \Log::error('Caught exception: ' . $response->body());
            throw $t;
        }
    }

    /**
     * メールヘッダーのセット
     *
     * @return void
     */
    private function _setHeader(): void
    {
        // カテゴリーのセット
        if (array_key_exists('category', $this->_mail_data) && !empty($this->_mail_data['category'])) {
            $this->_sendgrid_email->addCategory($this->_mail_data['category']);
        }
        // batch idがある場合は予約セット
        if (array_key_exists('batch_id', $this->_mail_data) && !empty($this->_mail_data['batch_id'])) {
            $this->_sendgrid_email->setBatchId($this->_mail_data['batch_id']);
            // 送信予定日時セット
            $dt = Carbon::parse($this->_mail_data['delivery_datetime']);
            $unix_delivery_datetime = (int) $dt->format('U');
            $this->_sendgrid_email->setGlobalSendAt($unix_delivery_datetime);
        }
        // 差出人アドレス, 差出人名セット
        $this->_sendgrid_email->setFrom($this->_mail_data['from_address'], $this->_mail_data['from_name']);
        // 件名セット
        $this->_sendgrid_email->setSubject($this->_mail_data['subject']);
        // テキストメール用に本文をセット
        $this->_sendgrid_email->addContent("text/plain", $this->_mail_data['mail_body']);
        // HTMLメール用に本文をセット
        $this->_sendgrid_email->addContent(
            "text/html",
            $this->_mail_data['mail_body']
        );
    }

    /**
     * 予約配信用 batch idをSendGridから取得
     *
     * @return string
     */
    private function _createBatchId(): string
    {
        try {
            $response = $this->sendgrid->client->mail()->batch()->post();
            $body = json_decode($response->body(), true);
            if (strpos($response->statusCode(), "2") !== 0) {
                throw $this->_getSendGridApiException($body);
            }
            if (array_key_exists('batch_id', $body) && !empty($body['batch_id'])) {
                return $body['batch_id'];
            }
        } catch (SendGridApiException $t) {
            \Log::error('Caught SendGridApiException: ' . $response->body());
            throw $t;
        } catch (\Throwable $t) {
            \Log::error('Caught exception: ' . $response->body());
            throw $t;
        }
    }

    /**
     * 予約配信リクエスト
     *
     * @return string
     */
    public function reserve(): string
    {
        $this->_mail_data['batch_id'] = $this->_createBatchId();
        $this->sendMails();
        return $this->_mail_data['batch_id'];
    }

    /**
     * 有効なbatch idかチェックする
     *
     * @param string $batch_id
     * @return void
     */
    public function validateBatchId(string $batch_id): void
    {
        try {
            $response = $this->sendgrid->client->mail()->batch()->_($batch_id)->get();
            $body = json_decode($response->body(), true);
            if (strpos($response->statusCode(), "2") !== 0) {
                throw $this->_getSendGridApiException($body);
            }
        } catch (SendGridApiException $t) {
            \Log::error('Caught SendGridApiException: ' . $response->body());
            throw $t;
        } catch (\Throwable $t) {
            \Log::error('Caught exception: ' . $response->body());
            throw $t;
        }
    }

    /**
     * 予約配信キャンセル(一時停止)
     *
     * @param string $batch_id
     * @return void
     */
    public function cancelReservation(string $batch_id): void
    {
        try {
            $request_body = [
                'batch_id' => $batch_id,
                'status' => "pause"
            ];
            $response = $this->sendgrid->client->user()->scheduled_sends()->post($request_body);
            $body = json_decode($response->body(), true);
            if (strpos($response->statusCode(), "2") !== 0) {
                throw $this->_getSendGridApiException($body);
            }
        } catch (SendGridApiException $t) {
            \Log::error('Caught SendGridApiException: ' . $response->body());
            throw $t;
        } catch (\Throwable $t) {
            \Log::error('Caught exception: ' . $response->body());
            throw $t;
        }
    }

    /**
     * SendGridApiException取得
     *
     * @param ?array
     * @return ?SendGridApiException
     */
    private function _getSendGridApiException(?array $body = []): SendGridApiException
    {
        if (!empty($body) && array_key_exists('errors', $body)) {
            return new SendGridApiException(500, $body['errors']);
        }
        // これは呼ばれない想定
        return new SendGridApiException(500, []);
    }

    /**
     * Category単位で統計情報取得
     *
     * @param string $category
     * @param string $aggregated_by day|month|week
     * @param string $start_date Y-m-d ... 2021-08-21
     * @param string $end_date Y-m-d
     * @return array
     *
     * @see https://sendgrid.kke.co.jp/docs/API_Reference/Web_API_v3/Stats/categories.html
     * @see https://github.com/sendgrid/sendgrid-php/blob/main/examples/categories/categories.php
     */
    public function getStatByCategories(
        array $categories,
        string $aggregated_by,
        string $start_date,
        string $end_date
    ): array {
        $query_params = [
            'start_date' => $start_date,
            'end_date' => $end_date,
            'aggregated_by' => $aggregated_by,
            'categories' => $categories,
        ];

        try {
            $response = $this->sendgrid->client->categories()->stats()->get(null, $query_params);
            $body = json_decode($response->body(), true);
            // 2xxでなかったらエラー
            if (strpos($response->statusCode(), "2") !== 0) {
                throw $this->_getSendGridApiException($body);
            }
            return $body;
        } catch (SendGridApiException $t) {
            \Log::error('Caught SendGridApiException: ' . $response->body());
            throw $t;
        } catch (\Throwable $t) {
            \Log::error('Caught exception: ' . $response->body());
            throw $t;
        }
    }
}

 

Amazonおすすめ

iPad 9世代 2021年最新作

iPad 9世代出たから買い替え。安いぞ!🐱 初めてならiPad。Kindleを外で見るならiPad mini。ほとんどの人には通常のiPadをおすすめします><

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)