はじめに
ユーザー招待機能の詳細設計と実装(1)招待メール送信フォーム について投稿します。
Web業界実務未経験での転職活動用にポートフォリオとしてはじめて作成したWebアプリ開発のオリジナルの機能として 家族ユーザー招待機能 を実装したときのものです。
※ 作成したポートフォリオは、絵本を読み聞かせしたことの記録・管理を、家族と共有できるWebアプリケーションです(作成期間は、2021年2月末〜7月)
投稿内容(全 5 回)
本連載は以下の順序で投稿します。
- ユーザー招待機能の概要と基本設計
-
ユーザー招待機能の詳細設計と実装
- (1) 招待メール送信フォーム
- (2) マイグレーションファイルからのテーブル作成
- (3) 招待メール送信処理
- (4) 招待メールのテンプレート
- (5) ユーザー登録フォームと登録処理
今回は、ユーザー招待機能の詳細設計と実装(1) 招待メール送信フォーム についてです。
目次
以下、本記事の目次です。
使用技術、サービスなど
- PHP 7.4.13
- Laravel 6.20.20
- MySQL 8.0.23
- MailHog(開発者向けのメールテストツール、開発環境)
1. ルーティングの追加
ルーティングを追加します。機能に必要なルーティングは、以下の 4 つです。
① 招待メール送信フォーム表示処理
② 招待メール送信処理
③ 招待用の登録フォーム表示処理
④ 招待用のユーザー登録処理
1-1. ルーティングの編集
以下のファイルを編集します。
<?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::get
( Route
ファサードの get
というメソッド)に、2つの引数を渡します。
- 第一引数には、文字列を渡すと URL は以下になります。
http://localhost:8880/invite
- 第二引数には、どのコントローラーで何のメソッドを実行するのかを文字列で渡します。
- コントローラー名
@
メソッド名という書き方です。 -
/invite
というURLに GET リクエスト(ブラウザなどからのアクセス)があったら、InviteController
のshowLinkRequestForm
アクションメソッドを動かす、ということが定義されます。
- コントローラー名
② 招待メール送信処理
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
- アクションメソッドは
InviteController
のshowLinkRequestForm
- 名前付きルートは
-
② 招待メール送信処理
- 名前付きルートは
invite.email
- アクションメソッドは
InviteController
のsendInviteFamilyEmail
- 名前付きルートは
-
③ 招待用の登録フォーム表示処理
- 名前付きルートは
register.invited.{token}
- アクションメソッドは
RegisterController
のshowInvitedUserRegistrationForm
- 名前付きルートは
-
④ 招待用のユーザー登録処理
- 名前付きルートは
register.invited
- アクションメソッドは
RegisterController
のregisterInvitedUser
- 名前付きルートは
2. コントローラ & 各アクションメソッドの作成
コントローラの作成(InviteController
)をして、 アクションメソッドの作成(showLinkRequestForm
メソッド)をします。
2-1. コントローラの作成(InviteController
)
下記コマンドで InviteController
を作成します。
php artisan make:controller InviteController
2-2. アクションメソッドの作成(showLinkRequestForm
メソッド)
<?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 を作成します。
3-1. 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 送信することで、以下の InviteController
の sendInviteFamilyEmail
アクションメソッドが処理されます。
# 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. 家族招待ボタンの設置
家族設定 ページから、 招待メール送信フォーム へ遷移可能にします。
4-1. 家族招待ボタンの設置
家族設定 ページに、 家族招待ボタン を2カ所設置します。
@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. 設置した家族招待ボタンの確認
設置した画面上部と家族一覧下部のボタンについて、イメージとコードを確認します。
-
確認: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>
-
確認: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. 招待メール送信フォームの確認
家族設定 ページの +招待する ボタンを押すと 招待メール送信フォーム に遷移するところまで完成しました。
おわりに
今回は、ユーザー招待機能の詳細設計と実装(1) 招待メール送信フォーム についてでした。
次回は、(2) マイグレーションファイルからのテーブル作成 についてです。
ありがとうございました。