【Laravel6 / MailHog】ユーザー招待機能の詳細設計と実装(3)招待メール送信処理

# **はじめに**

ユーザー招待機能の詳細設計と実装(3) 招待メール送信処理 について投稿します。

Web業界実務未経験での転職活動用にポートフォリオとしてはじめて作成したWebアプリ開発のオリジナルの機能として **家族ユーザー招待機能** を実装したときのものです。

※ 作成したポートフォリオは、絵本を読み聞かせしたことの記録・管理を、家族と共有できるWebアプリケーションです（作成期間は、2021年2月末〜7月）

https://qiita.com/ryamate/items/c3b4f63f8d94ab090bf8

## 投稿内容（全5回）

本連載は以下の順番で投稿します。

- [招待ユーザー機能の概要と基本設計](https://qiita.com/ryamate/items/751b06dc95ab9b24e361)
- **招待ユーザー機能の詳細設計と実装**
    - [(1) 招待メール送信フォーム](https://qiita.com/ryamate/items/a1d0a5b1db9d11a69671)
    - [(2) マイグレーションファイルからのテーブル作成](https://qiita.com/ryamate/items/bfca6024d09858351c83)
    - **(3) 招待メール送信処理**
    - [(4) 招待メールのテンプレート](https://qiita.com/ryamate/items/9392ba2a6e6351544e7b)
    - [(5) ユーザー登録フォームと登録処理](https://qiita.com/ryamate/items/57a94656134870d31edb)

今回は、**ユーザー招待機能の詳細設計と実装(3) 招待メール送信処理** についてです。 

## 目次

以下、本記事の目次です。
1. [Mailable クラスを継承したクラスの作成](#1-mailable-クラスを継承したクラスの作成)
1. [通知クラスの作成](#2-通知クラスの作成)
1. [Invite モデルへのメソッドの追加](#3-invite-モデルへのメソッドの追加)

## 使用技術、サービスなど

- PHP 7.4.13
- Laravel 6.20.20
- MySQL 8.0.23
- [MailHog](https://github.com/mailhog/MailHog)（開発者向けのメールテストツール、開発環境）

# 1. Mailable クラスを継承したクラスの作成

`Mailable` クラスを継承した `BareMail` クラスを作成します。

Laravel では、メールを取り扱うクラスが複数ありますが、`Mailable` クラスを利用します。`Mailable` クラスを選択する理由は、 HTML メールではなくテキストメールを送るには`Mailable`クラスを使う必要があるためです。

https://readouble.com/laravel/6.x/ja/mail.html

（このアプリの作成の際は「メールを利用したパスワード再設定」機能追加時に作成したファイルを、家族招待通知でも再利用しました。）

## 1-1. BareMail.php の作成

以下コマンドを実行して、 **backend/app/Mail/** ディレクトリ以下に、 **BareMail.php** を作成します。

```bash
docker-compose exec app php artisan make:mail BareMail
```

## 1-2. BareMail.php の編集

作成されたファイルを編集します。

```php:編集：backend/app/Mail/BareMail.php
<?php

namespace App\Mail;

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

class BareMail 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;
    }
}
```

メールの種類ごとの細かい設定は持たせず、空の設定のメールとして使用していくため、 `build` メソッドでは何も設定せず、そのまま自分自身を返します。

宛先や件名、使用するテンプレートなどは、次に作成する通知クラスで設定します。

# 2. 通知クラスの作成

通知クラス（`InvitationFamilyNotification` クラス）を作成し、 `BareMail` クラスを用いたメール送信メソッドを追加します。

## 2-1. InvitationFamilyNotification.php の作成

以下コマンドを実行して、 **backend/app/Notifications/** ディレクトリ以下に、 **InvitationFamilyNotification.php** を作成します。

```bash
docker-compose exec app php artisan make:notification InvitationFamilyNotification
```

## 2-2. InvitationFamilyNotification.php の編集

作成されたファイルを編集します。

```php:編集：backend/app/Notifications/InvitationFamilyNotification.php
<?php

namespace App\Notifications;

use App\Mail\BareMail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class InvitationFamilyNotification extends Notification
{
    use Queueable;

    public $token;
    public $mail;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct(string $token, BareMail $mail)
    {
        $this->token = $token;
        $this->mail = $mail;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \\Illuminate\\Notifications\\Messages\\MailMessage
     */
    public function toMail($notifiable)
    {
        return $this->mail
            ->from(config('mail.from.address'), config('mail.from.name'))
            ->to($notifiable->email)
            ->subject('[よんで] 家族招待')
            ->text('emails.invite')
            ->with([
                'url' => route('register.invited.{token}', [
                    'token' => $notifiable->token,
                ]),
            ]);
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}
```

## 2-3. 参考

### コンストラクタインジェクション

クラスのインスタンスをコンストラクタで注入(DI)することを、コンストラクタインジェクションと言う。

```php
public $token;
public $mail;
```

`InvitationFamilyNotification` クラスに `$token` プロパティと `$mail` プロパティを定義している。

```php
public function __construct(string $token, BareMail $mail)
{
    $this->token = $token;
    $this->mail = $mail;
}
```

`__construct` メソッドで、引数として、文字列の `$token`と、`BaraMail` クラスのインスタンスの `$mail` を、それぞれプロパティに代入している。

### toMail メソッド

`toMail` メソッド内で、メールの具体的な設定を行なっている。

```php
public function toMail($notifiable)
{
    return $this->mail
        ->from(config('mail.from.address'), config('mail.from.name'))
        ->to($notifiable->email)
        ->subject('[よんで] 家族招待')
        ->text('emails.invite')
        ->with([
            'url' => route('register.invited.{token}', [
                'token' => $notifiable->token,
            ]),
        ]);
}
```

### from メソッド

`Mailable` クラスの `from` メソッドに以下の引数を渡す。

- 第一引数には、送信元メールアドレス
- 第二引数には、メールの送信者名(省略可)

参考：[from メソッドの使用 - Laravel公式](https://readouble.com/laravel/6.x/ja/mail.html#configuring-the-sender)

引数については、`config` 関数を使って、 **backend/config/mail.php** の以下の値を取得している。

```php:確認：backend/config/mail.php
<?php

return [
// 略
    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
        'name' => env('MAIL_FROM_NAME', 'Example'),
    ],
// 略
];
```

`address`、`name`それぞれについて、 `env` 関数を使って取得した環境変数の値になっている。（ `env` 関数の第二引数は、第一引数の環境変数が存在しない場合のデフォルト値）

- 確認：**backend/.env**

```bash
APP_NAME=Yonde

# 省略

MAIL_FROM_NAME="${APP_NAME}"
MAIL_FROM_ADDRESS=no-reply@example.com
```

### to メソッド

```php
->to($notifiable->email)
```

`to`メソッドには、送信先メールアドレスを渡す。

`$notifiable` には、メール送信先となる `User` モデルが代入されているので、 `$notifiable->email` で、メール送信先ユーザーのメールアドレスを取得している。

### subject メソッド

```php
->subject('[よんで] 家族招待')
```

`subject`メソッドには、メールの件名を渡す。

### text メソッド

```php
->text('emails.invite')
```

`text`メソッドは、テキスト形式のメールを送る場合に使うメソッド。

引数で、メールのテンプレートを指定する。 `'emails.invite'` とすることで、**backend/resources/views/emails/** ディレクトリの **invite.blade.php** がテンプレートとして使用される。

### with メソッド

```php
->with([
    'url' => route('register.invited.{token}', [
        'token' => $notifiable->token,
    ]),
```

テンプレートとなる Blade に渡す変数を、 `with` メソッドに連想配列形式で渡す。

キー `url` の値には、 `route` 関数を使って `register.invited.{token}` のルーティングをセットしている。

- `register.invited.{token}` のルーティングは、以下のとおり。

```bash
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
| Domain | Method    | URI                               | Name                           | Action                                                                        | Middleware                                           |
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
// 略
|        | GET|HEAD  | register/invited/{token}          | register.invited.{token}       | App\\Http\\Controllers\\Auth\\RegisterController@showInvitedUserRegistrationForm  | web,guest                                            |
// 略

```

# 3. Invite モデルへのメソッドの追加

Invite モデルを作成して、メソッドを追加します。

## 3-1. Invite モデルの作成

下記コマンドを実行して、Invite モデルを作成します。

以下コマンドを実行して、 **backend/app/** ディレクトリ以下に、 **Invite.php** を作成します。

```bash
docker-compose exec app php artisan make:model Invite
```

## 3-2. Invite モデルへのメソッドの追加

**Invite** モデルに `sendInvitationFamilyNotification` メソッドを定義します。

```php:編集：backend/app/Invite.php
<?php

namespace App;

use App\Mail\BareMail;
use Illuminate\Database\Eloquent\Model;
use App\Notifications\InvitationFamilyNotification;
use Illuminate\Notifications\Notifiable;

class Invite extends Model
{
    use Notifiable;

    protected $fillable = [
        'family_id',
        'email',
        'token',
    ];

    // 家族招待通知送信
    public function sendInvitationFamilyNotification($token)
    {
        $this->notify(new InvitationFamilyNotification($token, new BareMail()));
    }
}
```

先ほど作成した通知クラスである `InvitationFamilyNotification` クラスのインスタンスを生成して、 `notify` メソッドに渡すことにより、次で作成するテキストメールが家族ユーザー招待メールとして送信されるようになります。

# おわりに

今回は、**招待ユーザー機能の詳細設計と実装(3) 招待メール送信処理** についてでした。
次回は、 **[(4) 招待メールのテンプレート](https://qiita.com/ryamate/items/9392ba2a6e6351544e7b)** についてです。

ありがとうございました。