【Laravel】Mailableを使ってメールを送信する方法。データや画像を添付する方法や入力データをメール本文に渡す方法

Mailableを使ってメールを送信する方法について。

[【Laravel】Localhostでgmailを使ってメールを送信する方法。Mailableを使わない方法と使う方法](https://qiita.com/yuta-38/items/5db343324b967046f7e1)の続きです。

基本的な設定や各記述の意味は上記リンク参照。


##目次

1. [.envの設定](#1-envの設定)
2. [Mailableクラスの作成](#2-mailableクラスの作成)
    3. [Mailableで使える主なメソッド](#mailableで使える主なメソッド)
4. [コントローラの作成](#3-コントローラの作成)
5. [ルーティングの作成](#4-ルーティングの作成)
6. [コンタクトフォームの作成（view）](#5-コンタクトフォームの作成view)
7. [メール内容の作成（view）](#6-メール内容の作成view)
8. [サーバーを起動とメール送信処理の実行](#7-サーバーを起動とメール送信処理の実行)
9. [メールにデータを添付する方法](#メールにデータを添付する方法)
10. [メール本文に画像を埋め込む方法](#メール本文に画像を埋め込む方法)
11. [メール本文にデータを渡す方法](#メール本文にデータを渡す方法)
    12. [Controllerの変更](#1-controllerの変更)
    13. [Mailableクラスの変更](#2-mailableクラスの変更)
    14. [コンタクトフォームのview変更](#3-コンタクトフォームのview変更)
    15. [メール本文のview変更](#4-メール本文のview変更)
    16. [メール送信結果の確認](#5-メール送信結果の確認)


<br>
##1. .envの設定
Gmailを送信元のメールサーバーとして使うため、環境変数の設定が必須。

ルートディレクトリ直下の.envの下記部分に追記する。

```:デフォルト
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
```

　　　↓

```:変更後
MAIL_MAILER=smtp  
MAIL_HOST=smtp.gmail.com     //gmailを指定
MAIL_PORT=587                //メッセージ送信のポート（465ではない）
MAIL_USERNAME=xxxx@gmail.com   //gmailアドレス
MAIL_PASSWORD=xxxxxxxxxxxxxxxx  //16桁のPW
MAIL_ENCRYPTION=tls          //tls通信
#MAIL_FROM_ADDRESS=null      //gmailの場合不要
MAIL_FROM_NAME="送信者の名前"    //メールの送信者に表示される名前
```



<br>
##2. Mailableクラスの作成

・`php artisan make:mail [Mailableクラス名]`

**▼実例**

```
$ php artisan make:mail ContactReply
Mail created successfully.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/522b8d17-4e3e-0387-12f3-f2d9fa997033.png)

**▼ファイルを編集**

```php:ContactReply.php（編集後）
<?php

namespace App\Mail;

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

class ContactReply extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        //以下追記
        return $this->view('emails.text')
                    ->subject('メールの標題');
    }
}
```

中にある関数は、`__construct`と`build`の２つのみ。
今回は、build関数にメール本文の処理を記述。

>宛先はコントローラに記述する。


<br>
・`Illuminate\Bus\Queueable`
キュー関連のメソッド（接続指定など）をもつクラス。

・`Illuminate\Contracts\Queue\ShouldQueue`
非同期で実行するため、ジョブをキューへ投入することをLaravelへ知らせるクラス。

・`Illuminate\Mail\Mailable`
メール送信処理を指定するメソッドを持つクラス。


・`Illuminate\Queue\SerializesModels`
Eloquentモデル（モデルのこと）をシリアライズするメソッドを持つクラス。

詳しいことは説明が見つからずよくわからなかったが、Mailableクラスとキューに関係するクラスのuse宣言をしている。


<br>
**・`__construct()`**
コンストラクタ。クラスをインスタンス化した初回時に実行する処理を記述する。

**▼コンストラクタの使用例**

コントローラのsendアクションから渡されたデータを受け取り、プロパティに代入する場合などに使える。

```php:Controllerのsendアクション
$params = array();
$params['name'] = 'your name';

Mail::to('送信先メールアドレス')->send(new ContactReply($params));
```

　↓　インスタンス生成時にデータ`$paramas`を渡す

```php:ContactReply.php
public function __construct($params)
{
   $this->params = $params;
}
```

paramsプロパティに`$params`が入る。



<br>
**・`build()`**
build関数の中にメール本文となるビューの指定などを記述する。

```php:Mailableのbuildメソッドの例
    public function build()
    {
        return $this->view('emails.text')
                    ->subject('メールの標題');
    }
```

・`view('emails.text')`
メール本文になるビューを指定。

・`subject('メールの標題')`
メールのタイトルを指定。


<br>
###Mailableで使える主なメソッド

| メソッド | 概要 | 使用例 |
|:-:|:--|:--|
| view('ビュー')  | メール本文となるビューを指定  | view('emails.text')  |
| subject('')  | メールのタイトルを指定  |  subject('お問い合わせ') |
| with([])  | データを渡す  | `with(['orderName' => $this->order->name])`  |
| attach('ファイルパス')  | データを添付する。第２引数でファイル名やmimeタイプを指定可能  | attach(public_path('img/cat.jpg')  |
| attachFromStorage('ファイルパス')  | ファイルシステム（オンラインストレージ）のデータを添付する  | attachFromStorage('/path/to/file')  |
| text('ビュー')  | 平文で表示するビューを指定  |  text('emails.text_plain') |

・平文テキストとは？
暗号化していないデータ。指定したビューの内容をそのまま送る。


<br>
##3. コントローラの作成

・`$ php artisan make:controller コントローラ名`

**▼実例**

```
$ php artisan make:controller MailableController
Controller created successfully.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/63e8ba22-1aab-ab85-8faf-2f31ebf3716f.png)



```php:MailableController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Mail;                   //追記
use App\Mail\ContactReply; //追記

class MailableController extends Controller
{
    //以下追記
    public function index() //コンタクトフォームの表示
    {
        return view('contact.index');
    }

    public function send(Request $request)  //メールの自動送信設定
    {
        Mail::to('hlaupcpky38@gmail.com')
              ->send(new ContactReply());

        return back()->withInput($request->only(['name']))
                     ->with('sent', '送信完了しました。');    //送信完了を表示
    }
}
```

基本的な記述はMailableを使用しないコントローラと同じ。

**▼変更点**

・`use App\Mail\ContactReply;`
生成したMailableクラスを使う宣言をする。

・`send(new ContactReply())`
sendメソッドの引数でMailableクラスのインスタンスを渡す。



<br>
##4. ルーティングの作成
ルーティングに作成したコントローラのアクションを登録する。

routes > web.php　に以下を追記。

```php:web.php
Route::get('contact2', 'MailableController@index');
Route::post('contact2', 'MailableController@send');
```

・1行目
URL　/contact2にアクセスしたときに、ContactControllerのindexアクションを実行する。

・2行目
/contact2ページでpostメソッドが使われたとき、ContactControllerのsendアクションを実行する。

formのactionでこのパスを指定しておく。


<br>
####（注意点）Laravel8の場合
Laravel8の場合、デフォルトではコントローラの名前空間を完全なパスで表記しなければいけない。

省略表記するためには、app > Providers > RouteServiceProvider.phpのコメントアウトを解除する。（以下ページ参照）

＞[コメントアウトの解除場所](https://qiita.com/drafts#routeserviceproviderphp%E3%81%AE%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%82%A2%E3%82%A6%E3%83%88%E3%82%92%E8%A7%A3%E9%99%A4%E3%81%99%E3%82%8B)




<br>
##5. コンタクトフォームの作成（view）
/contactにアクセスしたときに表示するビューを作成する。

indexアクションで`view('contact.index')`を指定しているので、**resources > views > contact > index.blade.php** を作成する。

views配下ならどこでもいいが、contactディレクトリを作成するのはコンタクトフォームに様々な種類ができたときにまとめて保存しておくため。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/8c1d5a5a-a8c0-8839-5546-e496d9b99d5a.png)


```php:index.blade.php
@if ( Session::has('sent'))
<div>
    <p>{{old('name')}}さん、{{ session('sent') }}</p>
</div>
@endif

<form action="{{ url('contact2') }}" method="POST">
    @csrf
 
    <p>名前：<input type="text" name="name"></p>
 
    <input type="submit" value="送信">
</form>
```

submitボタンをクリックすると、actionで指定したcontact2のページにPOSTメソッドでアクセスし、ルーティングでコントローラのsendアクションを実行する。

・`@if ( Session::has('sent'))`
withメソッドで渡されたデータ(キー名:sent)が存在するなら以下のdivタグを表示する。

つまり、送信ボタンが押された場合にこのdivタグが表示される。

```php:Controllerのsentアクション
 return back()->withInput($request->only(['name']))
              ->with('sent', '送信完了しました。');
```

・old('name')
`withInput()`で渡されたデータ

・session('sent')
`with()`で渡されたデータ





<br>
##6. メール内容の作成（view）
送信されるメール本文となるビューを作成する。
コントローラのsendアクションでビューを`emails.text`と指定しているので、以下パスにファイルを作成する。

resources > views > emails > text.blade.php

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/b73e2921-76f8-78b9-1df1-d09629ed580a.png)


```php:text.blade.php
emailsディレクトリのtext.blade.phpの内容です。
```


<br>
##7. サーバーを起動とメール送信処理の実行

```
$ php artisan serve

http://127.0.0.1:8000/
```

http://127.0.0.1:8000/contact2 にアクセスする。

 ↓ contacts.index.blade.phpの内容が表示される

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/cb26a406-d0dc-7ecc-875f-57d83211f40d.png)|
|:--|

　↓ 「送信」をクリック

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/48fb99f0-76a9-48a2-2cfa-70b08fbde5d2.png)|
|:--|

<br>
**▼Gmailの受信内容**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/fc11c5e1-f404-8721-ca8c-a32ba1a90c5b.png)

ページも表示され、Gmailにもメッセージが届いている。

以上でMailableを使ったメールの送信が完了。


以降は応用編として。



<br>
##メールにデータを添付する方法
ファイルや画像をメールに添付する方法について。

Mailableの`attach`メソッドを使うことで簡単に添付できる。

・`attach('ファイルのパス')`

**▼パス指定の注意点**
パスはURIではなくディレクトリパスを指定する。

・OK：　public_path('img/cat.jpg')
戻り値はディレクトリパスになる。

・NG：　assets('img/cat.jpg')　　
戻り値はURI。


```php:ContactReply.php
    public function build()
    {
        //以下追記
        return $this->view('emails.text')
                    ->subject('メールの標題')
                    ->attach(public_path('img/cat.jpg'));
    }
```

・`attach(public_path('img/cat.jpg'))`
public_pathヘルパ関数はpublicディレクトリ配下のパスを指定することで完全なパスを返す。


###画像を設置する
今回の場合は、 public > img > cat.jpg
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/074d0724-2f68-dcd7-7fd4-23410e6a7178.png)

**▼メール送信結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/d0e3827f-bf75-a0b5-b77c-2995f246f66a.png)

画像の添付に成功。


<br>
###storageディレクトリを活用する方法
publicディレクトリは公開されているディレクトリのため、公開していないアセットを送りたい場合は、storageディレクトリに設置する方法がある。

・`attach(storage_path('ファイルパス'))`
ファイルパスにstorage配下のパスを指定すればOK。


<br>
###メール本文に画像を埋め込む方法
メールに添付する以外に、メール本文に画像を埋め込むことも可能。

```
<img src="{{ $message->embed(ファイルパス) }}" >
```

- `$message`はLaravelが自動生成する。定形として使用。
- `embed`メソッドでファイルパスを指定する

この場合も、`asset`ヘルパー関数でURLを指定するのではなく、ディレクトリのパスを指定する。


```php:text.blade.php
emailsディレクトリのtext.blade.phpの内容です。

<img src="{{ $message->embed(public_path('img/small-cat.jpg')) }}" alt="">
```

**▼メール本文**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/96a27278-48de-d6b3-d5a4-3721a63f6280.png)



<br>
##メール本文にデータを渡す方法
コンタクトフォームに入力されたデータをメール本文に渡す方法について。

変更する必要があるファイルは３つ。

1. Controller
2. Mailable
3. View

- コントローラで入力されたデータを受け取り、Mailableインスタンスに渡す。
- MailableインスタンスからViewにデータを渡す。
- Viewで受け取ったデータを表示する

といった流れとなる。


<br>
##1. Controllerの変更

```php:MailableController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Mail;                   
use App\Mail\ContactReply; 

class MailableController extends Controller
{
    public function index() //コンタクトフォームの表示
    {
        return view('contact.index');
    }

    public function send(Request $request)  //メールの自動送信設定
    {
        $data = $request->all(); //追記

        Mail::to('hlaupcpky38@gmail.com')
                ->send(new ContactReply($data)); //変更

        return back()->withInput()  //変更
                     ->with('sent', '送信完了しました。');    //送信完了を表示
    }
}
```

・`$data = $request->all();`
コンタクトフォームに入力された内容を受け取り、変数dataにいれる。

allメソッドでRequestの全データを取得する。


・`send(new ContactReply($data))`
Mailableのクラス（ContactReply）からインスタンスを作成した際に`$data`を渡すため引数に記載。

・`withInput()`
withInputの引数を削除。これまでは１つのデータ(name)のみを送信後の画面に渡していたが、すべてのデータを渡すようにする。

遷移後のページでデータを参照する時は`old('キー名')`とする。



<br>
##2. Mailableクラスの変更
コントローラから受け取ったデータ(`$data`)を受け取り、使えるようにする。

```php:
<?php

namespace App\Mail;

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

class ContactReply extends Mailable
{
    use Queueable, SerializesModels;
    public $data;  //追記
    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($data)　  //変更
    {
        $this->data = $data;　  //追記
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        //以下追記
        return $this->view('emails.message')
                    ->subject('メールの標題')
                    ->with('data', $this->data);　　  //追記
    }
}
```

・`public $data;`
コントローラからのデータを受け取るため、インスタンスプロパティを用意する。


・`$this->data = $data;`
`__construct`の引数で変数を指定し、受け取ったデータをインスタンスプロパティにセットする。


・`->with('data', $this->data);`
withメソッドでインスタンスプロパティのデータをviewに渡す。


<br>
##3. コンタクトフォームのview変更
コンタクトフォームのビューにメアドとメッセージの入力欄を追加する。

```php:index.blade.php
@if ( Session::has('sent'))
<div>
    <p>{{old('name')}}さん、{{ session('sent') }}</p>
</div>
@endif

<form action="{{ url('contact2') }}" method="POST">
@csrf

 <p>名前：<input type="text" name="name" value="{{old('name')}}"></p>

 <p>メールアドレス：<input type="text" name="email" value="{{old('email')}}"></p>

 <p>メッセージ：
     <textarea name="message">{{ old('message') }}</textarea></p>

 <input type="submit" value="送信する">
</form>
```

・`url('contact2')`
urlヘルパ関数で指定したURLの完全なURIを返す。

・`old('キー名')`
withやwithInputメソッドで渡されたデータを表示する。

上記では、送信ボタンをクリック後に入力データがそのまま残るように指定している。


<br>
##4. メール本文のview変更
メール本文に受け取ったデータを使って表示するコードを追記する。
今回は、新しいファイルを用意して記述する。

resources > views > emails > message.blade.php 

```php:message.blade.php
<p>名前：{{ $data['name'] }}さん</p>
<p>メールアドレス：{{ $data['email'] }}</p>
<p>---以下メッセージが送信されました---</p>
<p>{!! nl2br( $data['message'] ) !!}</p>
```

・`$data['キー名']`
Mailableの`->with('data', $this->data);`で渡されたデータを取得している。


・`{!! !!}`
htmlspecialcharsをエスケープせずに呼び出す。
よく見かける`{{ }}`はクロスサイトスクリプティング（XSS）防止のため一部の特殊な記号が実行されないように変換される。

ここで`{{!! !!}}`を使うのは、ユーザーが打ち込んだ内容をそのまま表示するため。

これがないと、改行が`<br />`として表示されてしまい読みづらい。


・`nl2br()`
改行を改行のまま表示するメソッド。
これがないと、改行がスペースに変換され繋がって表示されてしまう。


なので、**`{!! nl2br( ) !!}` はユーザーの入力データをそのまま表示する**ためのコード。



以上で修正が完了



<br>
##5. メール送信結果の確認

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/7b0261d6-6d71-63ec-f39d-4d36144906e2.png)|
|:--|

　↓送信する

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/6bfc8134-317f-5f7e-7b85-bd836a4837ab.png)|
|:--|

送信後も入力内容が表示されている。


<br>
**▼メールの受信内容**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/63af0773-0e59-9d42-8c2e-0a5654f952b7.png)

入力した内容がそのままメール本文に反映されている。