Laravelでメール送信を送信したい

今参加しているプログラミングスクールでの課題で、LaravelでECsiteを作っているのですが、注文が確定したときにメールを送信する機能をつけようと思いました。
それを備忘録ようにメモします。

## 作りたいもの
 - ECsite（の一部）
 - 商品の注文が確定したときに、「注文完了」のメールを送りたい
 - gmailのアドレスから送信する

## gmailの二段階認証の設定とアプリパスワードの発行
送信元のgmailのアカウントの二段階認証がOFFの場合はONにします。
googleアカウント -> (左側の一覧から)セキュリティ -> 2段階認証
に進み、指示に従って設定を済ませてください。</br>
<img width="885" alt="security.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422722/8a52aff9-c5df-cc10-32bd-5d57956ce28e.png">


そして再び「セキュリティ」画面に戻ると、「2段階認証」がオンになっている下で、「アプリパスワード」という項目があると思うので、それをクリックします。
認証が完了するとアプリパスワードの生成画面が出てくるので、「アプリを選択」->「メール」、「デバイスを選択」->「windowsパソコン」に設定します、デバイスの部分はお使いのデバイスに合わせてください。</br>

<img width="753" alt="appli_password.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422722/62da4072-20c4-b144-5e52-2242a339b20f.png">


そして生成を押すと、16桁のアプリパスワードが生成されるのでこれをコピーしておきます。</br>
<img width="722" alt="appli_password_produce.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422722/b6b0f798-d0f4-9e7b-9be2-2863a0820e66.png">



## メールドライバやFromアドレスの設定
ドライバ等の設定はlaravel/config/mail.phpで行っていますが、実際の記述は.envファイルに記述するようになっているので、laravel/.envを確認し、以下のように変更します。

```.env
MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=<送信元gmailのアドレス>   #gmailの場合、USERNAMEはFROM_ADDRESSと同じ
MAIL_PASSWORD=<先程のアプリパスワード>
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=<送信元gmailのアドレス>
MAIL_FROM_NAME=ECSiteSample   #メールの件名なのでご自由に
```

## Laravelのmailableクラスの作成
メール送信クラスであるmailableクラスを生成します。 クラス名は「SampleNotification」とします。

```
# artisanコマンドでmailableクラスを生成する
php artisan make:mail SampleNotification
```

## メール本文の作成
resources/viewにemailフォルダを作り、そこにsample_mail.blade.phpを作成します。メール毎に内容を変えたい部分は変数を置いておきます。

```php:sample_mail.blade.php
<!DOCTYPE html>
<html lang="ja">
    <style>
        body {
            background-color: #fffacd;
        }
        h1 {
            font-size: 16px;
            color: #ff6666;
        }
        #button {
            width: 200px;
            text-align: center;
        }
            #button a {
            padding: 10px 20px;
            display: block;
            border: 1px solid #2a88bd;
            background-color: #FFFFFF;
            color: #2a88bd;
            text-decoration: none;
            box-shadow: 2px 2px 3px #f5deb3;
        }
            #button a:hover {
            background-color: #2a88bd;
            color: #FFFFFF;
        }
    </style>
    <body>
        <p>{{$text}}</p></br>
        <p>購入商品は</p>
        <table>
        @foreach ($data as $values)
            <td>{{$values['product_name']}}</td>
            <td>{{$values['number']}}</td>
        @endforeach
        </table>
        </br>
        <p>ありがとうございました</p>
    </body>
</html>
```

## mailableクラスの変更
mailableクラスを以下のように変更します。送りたい内容に合わせて、適宜変更してください。

```php:SampleMail.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class SampleMail extends Mailable
{
    use Queueable, SerializesModels;

    protected $title;
    protected $text;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($name, $text, $data)
    {
        $this->title = sprintf('%s様', $name);
        $this->text = $text;
        $this->data = $data;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('emails.sample_mail')
                    ->subject($this->title)
                    ->with([
                        'text' => $this->text,
                        'data' => $this->data,
                      ]);
    }
}
```
__constructはコンストラクタで、メール処理が発動した際に引数をとって、それぞれをメンバ変数へ格納しています。
build()メソッド内では、view()メソッドでHTMLメールのビューをセット、subject()メソッドでメールのタイトルをセット、withメソッドでviewに渡す変数をセットしています。先ほどの本文で他に変数を設定した場合は、ここで値を渡してあげてください。

## コントローラーの設定
コントローラーにメールを送るように記述します。
ルーティングによって、注文ボタンが押されたらpostPurchaseCompleteメソッドを呼び出すようにし、以下の記述を加えます。

```php:TransactionController
$mail_name = $request->get('user_name');
$mail_text = $mail_name.' 様、ご購入ありがとうございました';
$data = $request->session()->get('ordered_data');
$mail_to = $request->get('mail_address');
Mail::to($mail_to)->send( new SampleMail($mail_name, $mail_text, $data) );
```
僕の場合は確認画面からPOSTで購入者氏名とメールアドレス、そしてsessionに購入データが

```
$data = { {'prodeut_name'=>'りんご', 'number'=>3}
          {'product_name'=>'バナナ', 'number'=>1} };
```
という形で入っているので、これをそのまま渡しています。
取り敢えず、先程のSampleMail.phpのconstructメソッドに渡した引数を、SampleMailクラスに渡せば大丈夫です。

これで注文が確定すると、ちゃんとメールが届きました！</br>
![complete.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/422722/b1df1730-9d4c-8314-e306-a526e2773c58.jpeg)


## うまくいかない場合
一度エラーが出たら

```
php artisan config:cache
```
によってキャッシュを削除しないと、ファイルを変更してもうまくいかない場合があるようです（この理由はよくわかりません...）