【Laravel6】ユーザー招待機能の詳細設計と実装(1)招待メール送信フォーム

# はじめに

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

Web業界実務未経験での転職活動用にポートフォリオとしてはじめて作成したWebアプリ開発のオリジナルの機能として **家族ユーザー招待機能** を実装したときのものです。
※ 作成したポートフォリオは、絵本を読み聞かせしたことの記録・管理を、家族と共有できるWebアプリケーションです（作成期間は、2021年2月末〜7月）

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

## 投稿内容（全 5 回）

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

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

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

## 目次

以下、本記事の目次です。

1. [ルーティングの追加](#1-ルーティングの追加)
1. [コントローラ ＆ 各アクションメソッドの作成](#2-コントローラ--各アクションメソッドの作成)
1. [招待メール送信フォームの Blade 作成](#3-招待メール送信フォームの-blade-作成)
1. [家族招待ボタンの設置](#4-家族招待ボタンの設置)
1. [招待メール送信フォームの確認](#5-招待メール送信フォームの確認)

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

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

# 1. ルーティングの追加

ルーティングを追加します。機能に必要なルーティングは、以下の 4 つです。

① 招待メール送信フォーム表示処理
② 招待メール送信処理
③ 招待用の登録フォーム表示処理
④ 招待用のユーザー登録処理

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/7ddaefd4-bea1-ccd0-5eb0-7ceb97a2ad44.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/f728bc07-f952-6db0-a1b4-98b4e660c945.png)



## 1-1. ルーティングの編集

以下のファイルを編集します。

```php:backend/routes/web.php
<?php

// 省略

Route::prefix('register')->name('register.')->group(function () {
    Route::get('/invited/{token}', 'Auth\RegisterController@showInvitedUserRegistrationForm')->name('invited.{token}');
    Route::post('/invited', 'Auth\RegisterController@registerInvitedUser')->name('invited');
    // 省略
});

// 省略

Route::get('invite', 'InviteController@showLinkRequestForm')->name('invite')->middleware('auth');
Route::post('invite', 'InviteController@sendInviteFamilyEmail')->name('invite.email')->middleware('auth');
```

<details><summary>参考： backend/routes/web.php（全文） </summary>
    
```php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Auth::routes(['verify' => true]);
Route::prefix('login')->name('login.')->group(function () {
    Route::get('/{provider}', 'Auth\LoginController@redirectToProvider')->name('{provider}');
    Route::get('/{provider}/callback', 'Auth\LoginController@handleProviderCallback')->name('{provider}.callback');
});

Route::prefix('register')->name('register.')->group(function () {
    Route::get('/invited/{token}', 'Auth\RegisterController@showInvitedUserRegistrationForm')->name('invited.{token}');
    Route::post('/invited', 'Auth\RegisterController@registerInvitedUser')->name('invited');
    Route::get('/{provider}', 'Auth\RegisterController@showProviderUserRegistrationForm')->name('{provider}');
    Route::post('/{provider}', 'Auth\RegisterController@registerProviderUser')->name('{provider}');
});

Route::get('/email/verified', 'Auth\VerificationController@verified');

Route::get('/', 'HomeController@home')->name('home');
Route::get('/about', 'HomeController@about')->name('about');

Route::resource('/picture_books', 'PictureBookController')->except(['index', 'edit', 'destroy', 'update', 'show'])->middleware('auth');
Route::prefix('picture_books')->name('picture_books.')->group(function () {
    Route::get('/search', 'PictureBookController@search')->name('search');
    Route::get('/{picture_book}', 'PictureBookController@show')->name('show');
    Route::middleware('auth')->group(function () {
        Route::delete('/{picture_book}', 'PictureBookController@destroy')->name('destroy');
        Route::get('/{picture_book}/edit', 'PictureBookController@edit')->name('edit');
        Route::match(['put', 'patch'], '/{picture_book}', 'PictureBookController@update')->name('update');
        Route::put('/{picture_book}/like', 'PictureBookController@like')->name('like');
        Route::delete('/{picture_book}/like', 'PictureBookController@unlike')->name('unlike');
    });
});

Route::prefix('users')->name('users.')->group(function () {
    Route::get('/edit', 'UserController@edit')->name('edit');
    Route::post('/update', 'UserController@update')->name('update');
    Route::get('/{name}/setting_profile', 'UserController@showSettingProfile')->name('show_setting_profile')->middleware('auth');
    Route::get('/{name}', 'UserController@show')->name('show');
    Route::get('/{name}/likes', 'UserController@likes')->name('likes');
    Route::get('/{name}/followings', 'UserController@followings')->name('followings');
    Route::get('/{name}/followers', 'UserController@followers')->name('followers');
    Route::middleware('auth')->group(function () {
        Route::put('/{name}/follow', 'UserController@follow')->name('follow');
        Route::delete('/{name}/follow', 'UserController@unfollow')->name('unfollow');
    });
});
Route::get('/tags/{name}', 'TagController@show')->name('tags.show');

Route::prefix('families')->name('families.')->group(function () {
    Route::get('/{id}', 'FamilyController@index')->name('index');
    Route::get('/{id}/bookshelf', 'FamilyController@bookshelf')->name('bookshelf');
});

Route::get('invite', 'InviteController@showLinkRequestForm')->name('invite')->middleware('auth');
Route::post('invite', 'InviteController@sendInviteFamilyEmail')->name('invite.email')->middleware('auth');
```
</details>

### 編集内容の補足

#### ① 招待メール送信フォーム表示処理
```php
Route::get('invite', 'InviteController@showLinkRequestForm')->name('invite')->middleware('auth');
```

`Route::get`（ `Route` ファサードの `get` というメソッド）に、2つの引数を渡します。
- 第一引数には、文字列を渡すと URL は以下になります。
    - `http://localhost:8880/invite`
- 第二引数には、どのコントローラーで何のメソッドを実行するのかを文字列で渡します。
    - コントローラー名`@`メソッド名という書き方です。
    - `/invite` というURLに GET リクエスト(ブラウザなどからのアクセス)があったら、`InviteController` の `showLinkRequestForm` アクションメソッドを動かす、ということが定義されます。

#### ② 招待メール送信処理
```php
Route::post('invite', 'InviteController@sendInviteFamilyEmail')->name('invite.email')->middleware('auth');
```

#### ③ 招待用の登録フォーム表示処理
```php
Route::prefix('register')->name('register.')->group(function () {
    Route::get('/invited/{token}', 'Auth\RegisterController@showInvitedUserRegistrationForm')->name('invited.{token}');
    // 省略
});
```

- URL は `prefix` メソッド部分と合わせて以下になります。
    - `http://localhost:8880/register/invited/{token}`

#### ④ 招待用のユーザー登録処理
```php
Route::prefix('register')->name('register.')->group(function () {
    // 省略
    Route::post('/invited', 'Auth\RegisterController@registerInvitedUser')->name('invited');
    // 省略
});
```

## 1-2. ルーティングの確認

ルーティングを確認するため、以下コマンドを実行します。

```bash
php artisan route:list
```

```bash:確認：ルーティング（抜粋）
# r_yamate @ mbp in ~/Documents/code/Yonde-app on git:doc/mod_erd x [7:21:46]
$ docker-compose exec app php artisan route:list
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
| Domain | Method    | URI                               | Name                           | Action                                                                        | Middleware                                           |
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
# 省略
|        | GET|HEAD  | invite                            | invite                         | App\Http\Controllers\InviteController@showLinkRequestForm                     | web,auth                                             |
|        | POST      | invite                            | invite.email                   | App\Http\Controllers\InviteController@sendInviteFamilyEmail                   | web,auth                                             |
# 省略
|        | POST      | register/invited                  | register.invited               | App\Http\Controllers\Auth\RegisterController@registerInvitedUser              | web,guest                                            |
|        | GET|HEAD  | register/invited/{token}          | register.invited.{token}       | App\Http\Controllers\Auth\RegisterController@showInvitedUserRegistrationForm  | web,guest                                            |
# 省略
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
```

※ `docker-compose` コマンドで実行しています。

<details><summary>参考：ルーティング（全文）</summary>

```bash:参考：ルーティング（全文）
# r_yamate @ mbp in ~/Documents/code/Yonde-app on git:doc/mod_erd x [7:21:46]
$ docker-compose exec app php artisan route:list
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
| Domain | Method    | URI                               | Name                           | Action                                                                        | Middleware                                           |
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
|        | GET|HEAD  | /                                 | home                           | App\Http\Controllers\HomeController@home                                      | web                                                  |
|        | GET|HEAD  | _debugbar/assets/javascript       | debugbar.assets.js             | Barryvdh\Debugbar\Controllers\AssetController@js                              | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | GET|HEAD  | _debugbar/assets/stylesheets      | debugbar.assets.css            | Barryvdh\Debugbar\Controllers\AssetController@css                             | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | DELETE    | _debugbar/cache/{key}/{tags?}     | debugbar.cache.delete          | Barryvdh\Debugbar\Controllers\CacheController@delete                          | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | GET|HEAD  | _debugbar/clockwork/{id}          | debugbar.clockwork             | Barryvdh\Debugbar\Controllers\OpenHandlerController@clockwork                 | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | GET|HEAD  | _debugbar/open                    | debugbar.openhandler           | Barryvdh\Debugbar\Controllers\OpenHandlerController@handle                    | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | GET|HEAD  | _debugbar/telescope/{id}          | debugbar.telescope             | Barryvdh\Debugbar\Controllers\TelescopeController@show                        | Barryvdh\Debugbar\Middleware\DebugbarEnabled,Closure |
|        | GET|HEAD  | about                             | about                          | App\Http\Controllers\HomeController@about                                     | web                                                  |
|        | GET|HEAD  | children/create                   | children.create                | App\Http\Controllers\ChildController@create                                   | web,auth                                             |
|        | POST      | children/store                    | children.store                 | App\Http\Controllers\ChildController@store                                    | web,auth                                             |
|        | DELETE    | children/{id}                     | children.destroy               | App\Http\Controllers\ChildController@destroy                                  | web,auth                                             |
|        | GET|HEAD  | children/{id}                     | children.show                  | App\Http\Controllers\ChildController@show                                     | web,auth                                             |
|        | GET|HEAD  | children/{id}/edit                | children.edit                  | App\Http\Controllers\ChildController@edit                                     | web,auth                                             |
|        | POST      | children/{id}/update              | children.update                | App\Http\Controllers\ChildController@update                                   | web,auth                                             |
|        | POST      | contact                           | contact.post                   | App\Http\Controllers\ContactController@post                                   | web                                                  |
|        | GET|HEAD  | contact                           | contact.show                   | App\Http\Controllers\ContactController@show                                   | web                                                  |
|        | GET|HEAD  | contact/confirm                   | contact.confirm                | App\Http\Controllers\ContactController@confirm                                | web                                                  |
|        | POST      | contact/confirm                   | contact.send                   | App\Http\Controllers\ContactController@send                                   | web                                                  |
|        | GET|HEAD  | contact/thanks                    | contact.complete               | App\Http\Controllers\ContactController@complete                               | web                                                  |
|        | POST      | email/resend                      | verification.resend            | App\Http\Controllers\Auth\VerificationController@resend                       | web,auth,throttle:6,1                                |
|        | GET|HEAD  | email/verified                    |                                | App\Http\Controllers\Auth\VerificationController@verified                     | web,auth                                             |
|        | GET|HEAD  | email/verify                      | verification.notice            | App\Http\Controllers\Auth\VerificationController@show                         | web,auth                                             |
|        | GET|HEAD  | email/verify/{id}/{hash}          | verification.verify            | App\Http\Controllers\Auth\VerificationController@verify                       | web,auth,throttle:6,1                                |
|        | GET|HEAD  | families/edit                     | families.edit                  | App\Http\Controllers\FamilyController@edit                                    | web,auth                                             |
|        | GET|HEAD  | families/setting                  | families.setting               | App\Http\Controllers\FamilyController@setting                                 | web,auth                                             |
|        | POST      | families/update                   | families.update                | App\Http\Controllers\FamilyController@update                                  | web,auth                                             |
|        | GET|HEAD  | families/{name}                   | families.index                 | App\Http\Controllers\FamilyController@index                                   | web,auth                                             |
|        | GET|HEAD  | families/{name}/bookshelf         | families.bookshelf             | App\Http\Controllers\FamilyController@bookshelf                               | web,auth                                             |
|        | GET|HEAD  | families/{name}/curious           | families.curious               | App\Http\Controllers\FamilyController@booksCurious                            | web,auth                                             |
|        | DELETE    | families/{name}/follow            | families.unfollow              | App\Http\Controllers\FamilyController@unfollow                                | web,auth                                             |
|        | PUT       | families/{name}/follow            | families.follow                | App\Http\Controllers\FamilyController@follow                                  | web,auth                                             |
|        | GET|HEAD  | families/{name}/read              | families.read                  | App\Http\Controllers\FamilyController@booksRead                               | web,auth                                             |
|        | GET|HEAD  | families/{name}/read_record       | families.read_record           | App\Http\Controllers\FamilyController@readRecord                              | web,auth                                             |
|        | GET|HEAD  | families/{name}/{picture_book}    | families.show                  | App\Http\Controllers\FamilyController@show                                    | web,auth                                             |
|        | GET|HEAD  | invite                            | invite                         | App\Http\Controllers\InviteController@showLinkRequestForm                     | web,auth                                             |
|        | POST      | invite                            | invite.email                   | App\Http\Controllers\InviteController@sendInviteFamilyEmail                   | web,auth                                             |
|        | POST      | login                             |                                | App\Http\Controllers\Auth\LoginController@login                               | web,guest                                            |
|        | GET|HEAD  | login                             | login                          | App\Http\Controllers\Auth\LoginController@showLoginForm                       | web,guest                                            |
|        | GET|HEAD  | login/guest                       | login.guest                    | App\Http\Controllers\Auth\LoginController@guestLogin                          | web,guest                                            |
|        | GET|HEAD  | login/{provider}                  | login.{provider}               | App\Http\Controllers\Auth\LoginController@redirectToProvider                  | web,guest                                            |
|        | GET|HEAD  | login/{provider}/callback         | login.{provider}.callback      | App\Http\Controllers\Auth\LoginController@handleProviderCallback              | web,guest                                            |
|        | POST      | logout                            | logout                         | App\Http\Controllers\Auth\LoginController@logout                              | web                                                  |
|        | POST      | password/confirm                  |                                | App\Http\Controllers\Auth\ConfirmPasswordController@confirm                   | web,auth                                             |
|        | GET|HEAD  | password/confirm                  | password.confirm               | App\Http\Controllers\Auth\ConfirmPasswordController@showConfirmForm           | web,auth                                             |
|        | POST      | password/email                    | password.email                 | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail         | web                                                  |
|        | POST      | password/reset                    | password.update                | App\Http\Controllers\Auth\ResetPasswordController@reset                       | web                                                  |
|        | GET|HEAD  | password/reset                    | password.request               | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm        | web                                                  |
|        | GET|HEAD  | password/reset/{token}            | password.reset                 | App\Http\Controllers\Auth\ResetPasswordController@showResetForm               | web                                                  |
|        | POST      | picture_books                     | picture_books.store            | App\Http\Controllers\PictureBookController@store                              | web,auth,can:create,App\PictureBook                  |
|        | GET|HEAD  | picture_books/create              | picture_books.create           | App\Http\Controllers\PictureBookController@create                             | web,auth,can:create,App\PictureBook                  |
|        | GET|HEAD  | picture_books/search              | picture_books.search           | App\Http\Controllers\PictureBookController@search                             | web                                                  |
|        | GET|HEAD  | picture_books/search_bookshelf    | picture_books.search_bookshelf | App\Http\Controllers\PictureBookController@searchBookshelf                    | web                                                  |
|        | PUT|PATCH | picture_books/{picture_book}      | picture_books.update           | App\Http\Controllers\PictureBookController@update                             | web,auth,can:update,picture_book                     |
|        | DELETE    | picture_books/{picture_book}      | picture_books.destroy          | App\Http\Controllers\PictureBookController@destroy                            | web,auth,can:delete,picture_book                     |
|        | GET|HEAD  | picture_books/{picture_book}      | picture_books.show             | App\Http\Controllers\PictureBookController@show                               | web,can:view,picture_book                            |
|        | GET|HEAD  | picture_books/{picture_book}/edit | picture_books.edit             | App\Http\Controllers\PictureBookController@edit                               | web,auth,can:update,picture_book                     |
|        | DELETE    | picture_books/{picture_book}/like | picture_books.unlike           | App\Http\Controllers\PictureBookController@unlike                             | web,auth                                             |
|        | PUT       | picture_books/{picture_book}/like | picture_books.like             | App\Http\Controllers\PictureBookController@like                               | web,auth                                             |
|        | GET|HEAD  | privacy                           | privacy                        | App\Http\Controllers\HomeController@privacy                                   | web                                                  |
|        | GET|HEAD  | read_records                      | read_records.index             | App\Http\Controllers\ReadRecordController@index                               | web,auth                                             |
|        | POST      | read_records                      | read_records.store             | App\Http\Controllers\ReadRecordController@store                               | web,auth                                             |
|        | GET|HEAD  | read_records/create               | read_records.create            | App\Http\Controllers\ReadRecordController@create                              | web,auth                                             |
|        | DELETE    | read_records/{read_record}        | read_records.destroy           | App\Http\Controllers\ReadRecordController@destroy                             | web,auth                                             |
|        | PUT|PATCH | read_records/{read_record}        | read_records.update            | App\Http\Controllers\ReadRecordController@update                              | web,auth                                             |
|        | GET|HEAD  | read_records/{read_record}        | read_records.show              | App\Http\Controllers\ReadRecordController@show                                | web,auth                                             |
|        | GET|HEAD  | read_records/{read_record}/edit   | read_records.edit              | App\Http\Controllers\ReadRecordController@edit                                | web,auth                                             |
|        | POST      | register                          |                                | App\Http\Controllers\Auth\RegisterController@register                         | web,guest                                            |
|        | GET|HEAD  | register                          | register                       | App\Http\Controllers\Auth\RegisterController@showRegistrationForm             | web,guest                                            |
|        | POST      | register/invited                  | register.invited               | App\Http\Controllers\Auth\RegisterController@registerInvitedUser              | web,guest                                            |
|        | GET|HEAD  | register/invited/{token}          | register.invited.{token}       | App\Http\Controllers\Auth\RegisterController@showInvitedUserRegistrationForm  | web,guest                                            |
|        | POST      | register/{provider}               | register.{provider}            | App\Http\Controllers\Auth\RegisterController@registerProviderUser             | web,guest                                            |
|        | GET|HEAD  | register/{provider}               | register.{provider}            | App\Http\Controllers\Auth\RegisterController@showProviderUserRegistrationForm | web,guest                                            |
|        | GET|HEAD  | tags/{name}                       | tags.show                      | App\Http\Controllers\TagController@show                                       | web                                                  |
|        | GET|HEAD  | terms                             | terms                          | App\Http\Controllers\HomeController@terms                                     | web                                                  |
|        | GET|HEAD  | users/create_password             | users.create_password          | App\Http\Controllers\UserController@createPassword                            | web,auth                                             |
|        | POST      | users/delete_data                 | users.delete_data              | App\Http\Controllers\UserController@deleteData                                | web,auth                                             |
|        | GET|HEAD  | users/edit                        | users.edit                     | App\Http\Controllers\UserController@edit                                      | web,auth                                             |
|        | GET|HEAD  | users/edit_email                  | users.edit_email               | App\Http\Controllers\UserController@editEmail                                 | web,auth                                             |
|        | GET|HEAD  | users/edit_password               | users.edit_password            | App\Http\Controllers\UserController@editPassword                              | web,auth                                             |
|        | GET|HEAD  | users/resign                      | users.resign                   | App\Http\Controllers\UserController@resign                                    | web,auth                                             |
|        | GET|HEAD  | users/resigned                    | users.resigned                 | App\Http\Controllers\UserController@resigned                                  | web,auth                                             |
|        | GET|HEAD  | users/setting_profile             | users.setting_profile          | App\Http\Controllers\UserController@settingProfile                            | web,auth                                             |
|        | POST      | users/store_password              | users.store_password           | App\Http\Controllers\UserController@storePassword                             | web,auth                                             |
|        | POST      | users/update                      | users.update                   | App\Http\Controllers\UserController@update                                    | web,auth                                             |
|        | POST      | users/update_email                | users.update_email             | App\Http\Controllers\UserController@updateEmail                               | web,auth                                             |
|        | POST      | users/update_password             | users.update_password          | App\Http\Controllers\UserController@updatePassword                            | web,auth                                             |
|        | GET|HEAD  | users/{name}                      | users.index                    | App\Http\Controllers\UserController@index                                     | web,auth                                             |
|        | GET|HEAD  | users/{name}/followers            | users.followers                | App\Http\Controllers\UserController@followers                                 | web,auth                                             |
|        | GET|HEAD  | users/{name}/followings           | users.followings               | App\Http\Controllers\UserController@followings                                | web,auth                                             |
|        | GET|HEAD  | users/{name}/liked                | users.liked                    | App\Http\Controllers\UserController@liked                                     | web,auth                                             |
|        | GET|HEAD  | users/{name}/likes                | users.likes                    | App\Http\Controllers\UserController@likes                                     | web,auth                                             |
|        | GET|HEAD  | users/{name}/read_record          | users.read_record              | App\Http\Controllers\UserController@readRecord                                | web,auth                                             |
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
```
</details>
        
- **① 招待メール送信フォーム表示処理**
    - 名前付きルートは `invite`
    - アクションメソッドは `InviteController` の `showLinkRequestForm`
- **② 招待メール送信処理**
    - 名前付きルートは `invite.email`
    - アクションメソッドは `InviteController` の `sendInviteFamilyEmail`
- **③ 招待用の登録フォーム表示処理**
    - 名前付きルートは `register.invited.{token}`
    - アクションメソッドは `RegisterController` の `showInvitedUserRegistrationForm `
- **④ 招待用のユーザー登録処理**
    - 名前付きルートは `register.invited`
    - アクションメソッドは `RegisterController` の `registerInvitedUser `

# 2. コントローラ ＆ 各アクションメソッドの作成

コントローラの作成（`InviteController`）をして、 アクションメソッドの作成（`showLinkRequestForm` メソッド）をします。

## 2-1. コントローラの作成（`InviteController`）

下記コマンドで `InviteController` を作成します。

```bash
php artisan make:controller InviteController
```

## 2-2. アクションメソッドの作成（`showLinkRequestForm` メソッド）

```php:編集：app/Http/Controllers/InviteController.php
<?php

namespace App\Http\Controllers;

use Auth;
use App\Invite;
use App\Mail\BareMail;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Notifications\Notifiable;
use App\Notifications\InvitationFamilyNotification;

class InviteController extends Controller
{
    use Notifiable;

    /**
     * 家族招待メール送信フォーム
     */
    public function showLinkRequestForm()
    {
        return view('invite');
    }
}
```

# 3. 招待メール送信フォームの Blade 作成

**招待メール送信フォーム** の Blade を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/cd31c0bd-a2c0-d4a0-7e95-f5089182c137.png)

## 3-1. invite.blade.php ファイルの作成・編集

**resources/views/** ディレクトリに **invite.blade.php** を作成して、編集します。

```php:作成・編集：resources/views/invite.blade.php
@extends('app')

@section('title', '家族招待メール送信-よんで-')

@section('content')

@include('nav')

<div class="bg-paper py-4">
    <div class="container" style="max-width: 540px">
        <h3 class="text-center">
            家族招待メール送信
        </h3>
        <p style="font-size: 14px;">
            　本棚の共有するために招待したい、家族のメールアドレスを入力してください。<br>
            　入力したメールアドレス宛てに、招待用のユーザー登録ページの案内をお送りします。
        </p>

        @if (Auth::id() === config('const.GUEST_USER_ID'))
        <p class="text-danger">
            ※ゲストユーザーは、家族招待メールを送信できません。
        </p>
        @endif

        <div class="card my-4 shadow-sm">
            <div class="card-body">

                @include('error_card_list')

                @if (session('status'))
                <div class="card-text alert alert-success">
                    {{ session('status') }}
                </div>
                @endif

                <form method="POST" action="{{ route('invite.email') }}">
                    @csrf
                    <div class="form-group">
                        <label for="email">メールアドレス</label>
                        <input class="form-control" type="email" id="email" name="email" required
                            placeholder="メールアドレスを入力" value="{{ old('email') }}"
                            {{ Auth::id() === config('const.GUEST_USER_ID') ? 'readonly' : '' }}>
                        <p class="text-muted small ml-1 mb-0">※招待したい家族のメールアドレスを入力してください。</p>
                        <p class="text-muted small ml-1">※メール送信後、24時間以内に登録してください。</p>
                    </div>

                    @if (Auth::id() !== config('const.GUEST_USER_ID'))
                    <button type="submit" class="btn btn-block btn-teal1 mt-4">
                        <b>送信する</b>
                    </button>
                    @endif
                    <button type="button" onClick="history.back()"
                        class="btn btn-block bg-white btn-outline-teal1 text-decoration-none text-teal1 mt-3">
                        <i class="fas fa-arrow-left mr-1"></i>戻る
                    </button>
                </form>
            </div>
        </div>
    </div>
</div>

@include('footer')

@endsection
```

### 編集内容の補足

メールアドレスを入力して、メール送信ボタンを押すと、 POST 送信されます。

```php
<form method="POST" action="{{ route('invite.email') }}">
```

POST 送信先は、名前付きルート`invite.email`としています。

ここに POST 送信することで、以下の `InviteController` の `sendInviteFamilyEmail` アクションメソッドが処理されます。

```bash
# r_yamate @ mbp in ~/Documents/code/Yonde-app on git:doc/mod_erd x [7:21:46]
$ docker-compose exec app php artisan route:list
+--------+-----------+-------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
| Domain | Method    | URI                     | Name                           | Action                                                                        | Middleware                                           |
+--------+-----------+-------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
# 省略
|        | POST      | invite                  | invite.email                   | App\Http\Controllers\InviteController@sendInviteFamilyEmail                   | web,auth                                             |
# 省略
+--------+-----------+-------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
```

# 4. 家族招待ボタンの設置

**家族設定** ページから、 **招待メール送信フォーム** へ遷移可能にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/7ddaefd4-bea1-ccd0-5eb0-7ceb97a2ad44.png)

## 4-1. 家族招待ボタンの設置

**家族設定** ページに、 **家族招待ボタン** を２カ所設置します。

```diff_php:編集：resources/views/families/setting.blade.php（全文）
@extends('app')

@section('title', '家族設定-よんで-')

@section('content')

@include('nav')

<header>
    <div class="bg-paper">
        <div class="container" style="max-width: 900px;">
            <nav aria-label="breadcrumb">
                <ol class="breadcrumb bg-paper small pl-0 mb-0">
                    <li class="breadcrumb-item">
                        <a href="{{ route('home') }}" class="text-teal1">
                            よんで
                        </a>
                    </li>
                    <li class="breadcrumb-item active" aria-current="page">
                        {{ $family->nickname }}ファミリーの家族設定
                    </li>
                </ol>
            </nav>
        </div>
    </div>
</header>

<div class="bg-paper py-4">
    <div class="container" style="max-width: 540px">
+        <h4>家族設定</h4>
+        <div class="row">
+            <div class="col-sm-8">
+                <h5>家族を招待しよう</h5>
+                <p class="pt-2 mb-0">
+                    子どもの絵本を読んだ記録を簡単に共有できます
+                </p>
+            </div>
+            <div class="col-sm-4 my-2">
+                <a class="btn btn-block bg-paper btn-outline-teal1 text-teal1" href="{{ route('invite') }}">
+                    <i class="fas fa-plus mr-1"></i><b>招待する</b>
+                </a>
+            </div>
+        </div>
        <img src="{{ asset('image/setting_family.png') }}" class="img-fluid mx-auto d-block" alt="" width="80%">
        @include('families.setting_tabs', [
        'hasUser' => false,
        'hasFamily' => true,
        ])
        <div class="card mt-2 p-4 shadow-sm">
            <div class="card-body py-2">
                <p class="card-title text-secondary small mb-1">ファミリーID</p>
                <p class="card-text">{{ $family->name }}</p>
            </div>
            <div class="card-body py-2">
                <p class="card-title text-secondary small mb-1">ファミリーネーム</p>
                <p class="card-text">{{ $family->nickname }}</p>
            </div>
            <div class="card-body pt-2">
                <p class="card-title text-secondary small mb-1">家族紹介</p>
                <p class="card-text">{!! nl2br(e($family->introduction, false)) !!}</p>
            </div>
            <a class="btn btn-block bg-white btn-outline-teal1 text-decoration-none text-teal1 mb-4"
                href="{{ route('families.edit') }}">
                家族設定を編集
            </a>

            <div class="card-body border-top">
                <p class="card-title text-secondary">家族一覧</p>
                {{-- ユーザー本人 --}}
                <div class="row">
                    <div class="col-3 d-flex justify-content-end">
                        <a href="" class="">
                            @if ($user->icon_path)
                            <img src="{{ asset($user->icon_path) }}" alt="プロフィール画像" class="bg-white border"
                                style="width: 50px; height: 50px; background-position: center center; border-radius: 50%; object-fit:cover;">
                            @else
                            <p><i class="far fa-user-circle fa-3x text-secondary"></i></p>
                            @endif
                        </a>
                    </div>
                    <div class="col-9">
                        <a href="{{ route("users.index", ["name" => Auth::user()->name]) }}"
                            class="card-text text-teal1 text-decoration-none">
                            <b>{{ $user->nickname }}</b>
                        </a>
                        <p class="card-text small text-secondary">
                            {{ $user->relation }}
                        </p>
                    </div>
                </div>

                {{-- ユーザーの家族 --}}
                @foreach ($familyUsers as $familyUser)
                <div class="row">
                    <div class="col-3 d-flex justify-content-end mt-3">
                        <a href="{{ route("users.index", ["name" => $familyUser->name]) }}">
                            @if ($familyUser->icon_path)
                            <img src="{{ asset($familyUser->icon_path) }}" alt="プロフィール画像" class="bg-white border"
                                style="width: 50px; height: 50px; background-position: center center; border-radius: 50%;object-fit:cover;">
                            @else
                            <p class="mb-0">
                                <i class="far fa-user-circle fa-3x text-secondary"></i>
                            </p>
                            @endif
                        </a>
                    </div>
                    <div class="col-9 mt-3">
                        <a href="{{ route("users.index", ["name" => $familyUser->name]) }}"
                            class="card-text text-teal1 text-decoration-none">
                            {{ $familyUser->nickname }}
                        </a>
                        <p class="card-text small text-secondary">
                            {{ $familyUser->relation }}
                        </p>
                    </div>
                </div>
                @endforeach

+                <a class="btn btn-block btn-outline-paper text-decoration-none text-teal1 px-0 mt-4"
+                    href="{{ route('invite') }}">
+                    <i class="fas fa-plus mr-1"></i>家族を招待する
+                </a>
            </div>

            <div class="card-body border-top">
                <p class="card-title text-secondary">お子さま一覧</p>
                @foreach ($children as $child)
                <div class="row">
                    <div class="col-3 d-flex justify-content-end mt-3">
                        <a href="{{ route('children.show', ['id' => $child->id]) }}">
                            @if(Carbon\Carbon::parse($child->birthday)->lte(Carbon\Carbon::now()->subYear())
                            && $child->gender_code === 2)
                            <img src="{{ asset('image/girl.png') }}" alt="プロフィール画像" class="bg-paper border"
                                style="width: 50px; height:50px;background-position: center;border-radius: 50%;object-fit:cover; margin-right:1px" />
                            @elseif(Carbon\Carbon::parse($child->birthday)->lte(Carbon\Carbon::now()->subYear()))
                            <img src="{{ asset('image/boy.png') }}" alt="プロフィール画像" class="bg-paper border"
                                style="width: 50px; height:50px;background-position: center;border-radius: 50%;object-fit:cover; margin-right:1px" />
                            @else
                            <img src="{{ asset('image/baby.png') }}" alt="プロフィール画像" class="bg-paper border"
                                style="width: 50px; height:50px;background-position: center;border-radius: 50%;object-fit:cover; margin-right:1px" />
                            @endif
                        </a>
                    </div>
                    <div class="col-9 mt-3">
                        <a href="{{ route('children.edit', ['id' => $child->id]) }}"
                            class="card-text text-teal1 text-decoration-none">
                            {{ $child->name }}
                        </a>
                        <p class="card-text d-flex flex-wrap align-items-center small text-secondary">
                            @if ($child->gender_code === 1)
                            <span class="badge badge-dark-mocha">
                                男の子
                            </span>
                            @elseif($child->gender_code === 2)
                            <span class="badge badge-mocha">
                                女の子
                            </span>
                            @endif

                            @if ($child->birthday !== null)
                            <span class="mx-1">/</span>
                            <span>
                                {{ Carbon\Carbon::parse($child->birthday)->diff(Carbon\Carbon::now())->format('%y歳%mヶ月') }}
                            </span>
                            <span class="mx-1">/</span>
                            <span>
                                {{ Carbon\Carbon::parse($child->birthday)->format("Y年m月d日") }}生まれ
                            </span>
                            @endif
                        </p>

                    </div>
                </div>
                @endforeach
                <a class="btn btn-block btn-outline-paper text-decoration-none text-teal1 px-0 mt-4"
                    href="{{ route('children.create') }}">
                    <i class="fas fa-plus mr-1"></i>お子さまを追加する
                </a>
            </div>

        </div>
    </div>
</div>

@include('footer')

@endsection
```

## 4-2. 設置した家族招待ボタンの確認

設置した画面上部と家族一覧下部のボタンについて、イメージとコードを確認します。

- 確認：**設置したボタン（画面上部）のイメージ**
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/43fcfa74-c6e4-ea35-ef4c-bcd60d6a8e27.png)

    ```php:確認：resources/views/families/setting.blade.php（「設置したボタン（画面上部）」抜粋）
    <h4>家族設定</h4>
    <div class="row">
        <div class="col-sm-8">
            <h5>家族を招待しよう</h5>
            <p class="pt-2 mb-0">
                子どもの絵本を読んだ記録を簡単に共有できます
            </p>
        </div>
        <div class="col-sm-4 my-2">
            <a class="btn btn-block bg-paper btn-outline-teal1 text-teal1" href="{{ route('invite') }}">
                <i class="fas fa-plus mr-1"></i><b>招待する</b>
            </a>
        </div>
    </div>
    ```

- 確認：**設置したボタン（家族一覧下部）のイメージ**
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/ee599c08-1056-3c45-f40a-541d6997b86c.png)

    ```php:確認：resources/views/families/setting.blade.php（「設置したボタン（家族一覧下部）」抜粋）
    <a class="btn btn-block btn-outline-paper text-decoration-none text-teal1 px-0 mt-4"
            href="{{ route('invite') }}">
        <i class="fas fa-plus mr-1"></i>家族を招待する
    </a>
    ```

# 5. 招待メール送信フォームの確認

**家族設定** ページの **＋招待する** ボタンを押すと **招待メール送信フォーム** に遷移するところまで完成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/7ddaefd4-bea1-ccd0-5eb0-7ceb97a2ad44.png)

# おわりに

今回は、**ユーザー招待機能の詳細設計と実装(1) 招待メール送信フォーム** についてでした。
次回は、**[(2) マイグレーションファイルからのテーブル作成](https://qiita.com/ryamate/items/bfca6024d09858351c83)** についてです。

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