5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

完走賞をとって Qiitan を自分にクリスマスプレゼントするAdvent Calendar 2022

Day 19

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

Last updated at Posted at 2022-12-18

はじめに

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

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

投稿内容(全 5 回)

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

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

目次

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

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

使用技術、サービスなど

  • PHP 7.4.13
  • Laravel 6.20.20
  • MySQL 8.0.23
  • MailHog(開発者向けのメールテストツール、開発環境)

1. ルーティングの追加

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

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

image.png

image.png

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

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

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');
参考: backend/routes/web.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');

編集内容の補足

① 招待メール送信フォーム表示処理

Route::get('invite', 'InviteController@showLinkRequestForm')->name('invite')->middleware('auth');

Route::getRoute ファサードの get というメソッド)に、2つの引数を渡します。

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

② 招待メール送信処理

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

③ 招待用の登録フォーム表示処理

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}

④ 招待用のユーザー登録処理

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

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

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

php artisan route:list
確認:ルーティング(抜粋)
# 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 コマンドで実行しています。

参考:ルーティング(全文)
参考:ルーティング(全文)
# 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                                             |
+--------+-----------+-----------------------------------+--------------------------------+-------------------------------------------------------------------------------+------------------------------------------------------+
  • ① 招待メール送信フォーム表示処理
    • 名前付きルートは invite
    • アクションメソッドは InviteControllershowLinkRequestForm
  • ② 招待メール送信処理
    • 名前付きルートは invite.email
    • アクションメソッドは InviteControllersendInviteFamilyEmail
  • ③ 招待用の登録フォーム表示処理
    • 名前付きルートは register.invited.{token}
    • アクションメソッドは RegisterControllershowInvitedUserRegistrationForm
  • ④ 招待用のユーザー登録処理
    • 名前付きルートは register.invited
    • アクションメソッドは RegisterControllerregisterInvitedUser

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

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

2-1. コントローラの作成(InviteController

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

php artisan make:controller InviteController

2-2. アクションメソッドの作成(showLinkRequestForm メソッド)

編集: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

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

resources/views/ ディレクトリに invite.blade.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 送信されます。

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

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

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

# 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

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

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

編集: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

    確認: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

    確認: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

おわりに

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

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

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?