Sendgridでメールを一斉送信したかった

環境:Laravel8,sendgrid

sendgridとはクラウドメール配信サービスです。

今日はこのサービスを利用してメール配信をしたいと思いました。
そしたらめちゃくちゃ苦労したので備忘録として書きます。

試行錯誤した過程も書いてあるので結論だけみたい人は <b>GuzzleでAPIをたたこう</b> まで飛ばしてください。

###やりたいこと
メールを一斉に個別送信したい。
一斉に個別送信とは、宛先を個人にしたいが送信は一括でやりたいということです。

普通に一斉送信すると宛先を全員指定して受信側でも全員の宛先を見れてしまうので個別におくりたいです。

ユーザー登録からAPIキーの発行までは調べれば簡単に出来るので省略します。

##はじまり
sendgridではweb APIを利用することでメールを送信できるそうです。

公式ドキュメントを見てましょう。

https://sendgrid.kke.co.jp/docs/Tutorials/A_Transaction_Mail/send_transaction_mail.html

[上記URLより引用]

>cURL
cURLコマンドを利用してWeb API経由でメール送信できます。[API_KEY]にはAPIキーを設定します。詳しいパラメータや制限事項についてはAPIリファレンスを参照してください。

>```
curl -X POST https://api.sendgrid.com/v3/mail/send \
     -d "JSONデータを指定" \
     -H "Authorization: Bearer [API_KEY]" \
     -H "Content-Type: application/json"
```
>-dパラメータには以下のようなJSONデータを指定します。

>```json
{
  "personalizations": [
    {
      "to": [
        {
          "email": "recipient_address@example.com"
        }
      ],
      "subject": "こんにちは！"
    }
  ],
  "from": {
    "email": "from_address@example.com"
  },
  "content": [
    {
      "type": "text/plain",
      "value": "テキストメールです！"
    }
  ]
}
```

これだけです。

APIの利用に慣れている方ならなるほどな～となるところでしょうが、
僕は今までURL叩けばjson形式で返してくれる簡単なAPIしか利用したことがなかったので非常に困惑しました。


上のを見てもわからないので取り敢えず”sendgrid laravel”のような単語で検索し、もっとわかりやすい記事はないかなと探しました。
そしてでてきた記事が以下のものです。

https://qiita.com/masuda-sankosc/items/651eb1382144c17c8321

ライブラリを利用して送信できるそうです。
これならAPIを理解していなくても使えそうですね

上記に習い以下のようなコードを書きました

```php
    public function sendMail_way1(){
        $email = new \SendGrid\Mail\Mail();
        $email->setFrom('testfrom@example.com');//送信元
        $email->setSubject("複数送信のテスト");
        $email->addTo('test1@example.com');//送信先
        $email->addTo('test2@example.com');//送信先
        $email->addTo('test3@example.com');//送信先
        $apiKey = getenv('SENDGRID_API_KEY');
        $sendGrid = new \SendGrid($apiKey);
        $email->addContent(
            "text/plain",
            "test mail"
        );
        $response = $sendGrid->send($email);

        if ($response->statusCode() == 202) {
            return View('mail.editMail');
        }

            dd('error');
    }
```

'$email->addTo' が２つあるのは複数似送信したいので試しに２つ送信先を指定しました。

これで実行してみましょう。

…

届きました。成功です。
これにて完了です…と言いたいところですが上記の方法では問題がありました。
届いたメールを見てましょう

注目すべきは送信先を示すToの部分

```
test1@example.com,test2@example.com,自分
```

となっています。
送信先全員のアドレスが含まれています。これを全員に送信するのは大問題です。
特に本番運用で顧客に一斉送信してたらと思うとゾッとしますね…


個別に送信するなら上記のやり方で大丈夫ですが、複数人に送る場合はやり方を変えなければなりません。

回避策としては送信先を複数指定するのを止め、sendMail自体をforeachかなんかで送信先ごとに実行するなどが考えられますがあまり美しくありません（コードが）。

なんとか一斉に個別送信できないものか…

https://sendgrid.kke.co.jp/docs/Tutorials/A_Transaction_Mail/send_with_substitution.html

公式ドキュメントに戻りAPI仕様書色々を眺めていると'personalizations'という配列が目に止まりました。
調べてみるとこの配列は

```
  "personalizations": [
    {
      "to": [
        {
          "email": "recipient_address@example.com"
        }
      ],
      "subject": "こんにちは！"
    }
  ]
```
このような形式をとりこのtoに指定したメールアドレス毎にメールを送れるそうです。

これを使えばに一斉に個別送信できる！！

とは思ったもののどうすればAPIを叩けるのか分かりませんでした。

結局APIからは逃れられないのか…

#GuzzleでAPIをたたこう
調べたらGuzzleというのがでてきました。
PHP用のHTTPクライアントライブラリだそうです。

Guzzleの形式はこんな感じ。

```PHP
// インスタンス作成
$client = new Client();

// リクエスト送信
$options = [
    // POSTデータは"form_params"に記載
    'form_params' => [
        'name' => 'tak-solder',
        'job' => 'engineer',
        'language' => [
            'php',
            'javascript'
        ]
    ]
];

//送信し、返り値を受け取っている。。
$response = $client->request('POST', '[APIのエンドポイント(URL)]', $options);
```
インスタンス作成
オプション設定
送信の3ステップ。


これならある程度は簡単にAPIを叩けます。

公式キュメントにあるこれを、

```
curl -X POST https://api.sendgrid.com/v3/mail/send \
     -d "JSONデータを指定" \
     -H "Authorization: Bearer [API_KEY]" \
     -H "Content-Type: application/json"
```

Guzzleの形式に沿って組み立てましょう。

...どうやって？

'-X POST https://api.sendgrid.com/v3/mail/send \'
これは送信時のエンドポイントとPOSTを設定すればいいのだろう。

問題は以下3つ
     -d "JSONデータを指定" \
     -H "Authorization: Bearer [API_KEY]" \
     -H "Content-Type: application/json"

これはなんだろう…？
おそらく$options に設定すればいいのだろうがどうやって設定するんですかね…？

これにはCurlの引値をちゃんと理解する必要がありました。

↓参考

https://qiita.com/takayukioda/items/edf371b3566bea64d046

調べるとまず-Hはヘッダー情報の付与だそう

```php
        $options = [
            // リクエストヘッダー
            'headers' => [
                     //←ここ、header情報
            ],
        ];
```

ここに書けば良さそう

```php
        $options = [
            // リクエストヘッダー
            'headers' => [
                'Authorization'=>'Bearer [API-key]',
                'Content-Type'=> 'application/json'
            ],
        ];
```
こんな感じに


-dはデータの指定ができるそう
ここに指定されたデータはRequest Bodyにはいる

body要素にはjsonを渡したいので

```php
        $options = [
            // リクエストヘッダー
            'headers' => [
                'Authorization'=>'Bearer [API-key]',
                'Content-Type'=> 'application/json'
            ],
            'body' => $json_content,
        ];
```
bodyにjsonを入れた変数を渡す。

渡すjsonデータは公式ドキュメントに則って以下のように
試行錯誤しながらコードをく見立てたものがこれ、

```PHP
    public function sendMail_way2(Request $request){
        // インスタンス作成
        $client = new Client();

        $json_content='{
            "personalizations": [
              {
                "to": [
                  {
                    "email": "test1@example.com"
                  }
                ],
                "subject": "Hello, World!"
              },
              {
                "to": [
                  {
                    "email": "test2@example.com"
                  }
                ],
                "subject": "Hello2, World2!"
              }
            ],
            "from": {
              "email": "testfrom@example.com"
            },
            "content": [
              {
                "type": "text/plain",
                "value": "Hello, World!"
              }
            ]
          }';

        // オプション設定
        $options = [
            // リクエストヘッダー
            'headers' => [
                'Authorization'=>'Bearer [API-key]',
                'Content-Type'=> 'application/json'
            ],
            'body' => $json_content,
        ];

        // リクエスト送信
        $response = $client->request('POST', 'https://api.sendgrid.com/v3/mail/send', $options);


        dd($response);
    }
```

返り値の$responseをddするとこんな感じ。

```
GuzzleHttp\Psr7\Response {#426 ▼
  -reasonPhrase: "Accepted"
  -statusCode: 202
  -headers: array:11 [▶]
  -headerNames: array:11 [▶]
  -protocol: "1.1"
  -stream: GuzzleHttp\Psr7\Stream {#423 ▶}
}
```

 Acceptedと表示され -statusCodeが202となっていれば成功。

最終的には上記の$json_contentを生成するコードを書けば完成。


#完
APIの叩き方を理解した僕に死角はありません。