家族ユーザー招待機能 の詳細設計と実装(5) - はじめてのWebアプリ開発を振り返る Part9

こんにちは!
スマレジ・テックファームのWebエンジニアやまてと申します。
はじめに
今回は、Web業界実務未経験での転職活動用にポートフォリオとして作成した『はじめてのWebアプリ開発を振り返る』記事です。
※ 作成したポートフォリオは、絵本を読み聞かせしたことの記録・管理を、家族と共有できるWebアプリケーションです
実装したオリジナルの機能の一つである「家族ユーザー招待」機能について、記事にします。
概要と基本設計
詳細設計と実装
(1) 家族招待メール送信フォームの作成
(5) ユーザー登録フォームと登録処理の作成【今回】
今回は、家族招待用の新規登録フォーム画面表示処理、家族ユーザー登録処理の作成手順です。

目次
- はじめに
- 1. ルーティングの確認
- 2. 「家族招待用の登録フォーム画面表示」処理の追加
- 3. 家族招待用の登録フォーム画面の作成
- 4. 「家族ユーザー登録」処理の追加
- 5. 家族招待用の登録フォーム画面の確認
- おわりに
使用技術、サービスなど
- フロントエンド
- HTML 5 / CSS 3
- Bootstrap 4.5.0
- JavaScript
- Vue.js 2.6.11
- jQuery 3.5.1
- HTML 5 / CSS 3
- バックエンド
- メール関連
- MailHog(開発者向けのメールテストツール、開発環境)
- SendGrid(メール配信サービス、本番環境)
1. ルーティングの確認
ルーティングの追加は、以下のステップで完了しています。
2. 「家族招待用の登録フォーム画面表示」処理の追加
app/Http/Controllers/Auth ディレクトリの RegisterController.php に showInvitedUserRegistrationForm メソッドを追加します。
- 編集:backend/app/Http/Controllers/Auth/RegisterController.php
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\User; use App\Family; use App\Child; use App\Invite; // 追加 use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Laravel\Socialite\Facades\Socialite; use Illuminate\Support\Str; use Illuminate\Validation\Rule; class RegisterController extends Controller { // 省略 // 「家族招待用の登録フォーム画面表示」処理の追加 public function showInvitedUserRegistrationForm(string $token) { $invite = Invite::where('token', $token)->first(); if (!isset($invite)) { abort(401); } return view('auth.invite_register', [ 'token' => $invite->token, 'family_id' => $invite->family_id, 'email' => $invite->email, ]); } // 省略 }
use App\Invite;を追加して、トークンを管理している Invite モデルからデータを取得できるようにしておきます。- トークンが Invite モデルにない場合は、 401 エラーを返すようにしました。

3. 家族招待用の登録フォーム画面の作成
家族招待用の登録フォーム画面の blade を作成します。以下の画面の作成です。

- 作成・編集:backend/resources/views/auth/invite_register.blade.php
@extends('app')
@section('title', '招待ユーザー登録-よんで-')
@section('content')
@include('auth.nav')
<div class="bg-paper py-4">
<div class="container" style="max-width: 540px">
<h3 class="text-center">
よんで の新規登録(家族招待用)
</h3>
<h4 class="text-center">
<b>ようこそ、 よんで へ。</b>
</h4>
<div class="card my-4 shadow-sm">
<div class="card-body">
@include('error_card_list')
<form method="POST" action="{{ route('register.invited') }}">
@csrf
<input type="hidden" name="token" value="{{ $token }}">
<input type="hidden" name="family_id" value="{{ $family_id }}">
<input type="hidden" name="email" value="{{ $email }}">
<p class="x-small">
(<span class="text-danger">*</span>は必須項目です)
</p>
<div class="form-group">
<label for="email">メールアドレス</label>
<input class="form-control" type="text" id="email" name="email" value="{{ $email }}" disabled
required>
</div>
<div class="form-group">
<label for="nickname">ユーザーネーム</label><span class="text-danger">*</span>
<input class="form-control" type="text" id="nickname" name="nickname" placeholder="ユーザーネームを入力"
required value="{{ old('nickname') }}">
<p class="text-muted small ml-1">50文字以内</p>
</div>
<div class="form-group">
<label for="password">パスワード</label><span class="text-danger">*</span>
<input class="form-control" type="password" placeholder="パスワードを作成" id="password" name="password"
required>
<p class="text-muted small ml-1">半角英数・記号:8文字以上</p>
</div>
<div class="form-group">
<label for="password_confirmation">パスワード(確認)</label><span class="text-danger">*</span>
<input class="form-control" type="password" placeholder="パスワードを確認" id="password_confirmation"
name="password_confirmation" required>
</div>
<label for="agree" class="small" role="button">
<span class="d-flex flex-wrap">
<span>
<input type="checkbox" id="agree" required>
<a href="{{ route('privacy') }}" class="text-teal1 ml-2" target="_blank"
title="プライバシーポリシーをブラウザの別画面で開く">プライバシーポリシー</a>
<span>を確認し、</span>
</span>
<span>同意</span>
<span>
<span>
<span>しました。</span><span class="text-danger">*</span>
</span>
</span>
</label>
<button type="submit" class="btn btn-block bg-pink text-decoration-none text-white mt-4">
<b>登録</b>
</button>
</form>
</div>
</div>
</div>
</div>
@include('footer')
@endsection
4. 「家族ユーザー登録」処理の追加
app/Http/Controllers/Auth ディレクトリの RegisterController.php に registerInvitedUser メソッドを追加します。
- 編集:backend/app/Http/Controllers/Auth/RegisterController.php
<?php // 省略 class RegisterController extends Controller { // 省略 // 「家族招待用のユーザー登録」処理 public function registerInvitedUser(Request $request) { if (!$invite = Invite::where('token', $request->token)->first()) { abort(401); } $request->validate([ 'nickname' => ['required', 'string', 'max:50'], 'email' => ['required', 'string', 'email', 'max:255', Rule::unique('users', 'email')->whereNull('deleted_at'),], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); $checkUniqueName = true; while ($checkUniqueName) { $userName = Str::random(16); $checkUniqueName = User::where('name', $userName)->exists(); } $user = User::create([ 'name' => $userName, 'nickname' => $request->nickname, 'email' => $request->email, 'email_verified_at' => now(), 'password' => Hash::make($request->password), 'family_id' => $request->family_id, ]); $this->guard()->login($user, true); $invite->delete(); return $this->registered($request, $user) ?: redirect($this->redirectPath()); } // 省略 }
- 一連の流れを通して、招待する側と同一の
family_idにしてユーザー登録することで、登録後、家族ユーザーとして本棚を共有できるようにしています。 $invite->delete();で招待用トークンは削除して、使えないようにしています。
5. 家族招待用の登録フォーム画面の確認
前回ブラウザで確認した MailHog の受信ボックスに届いているメールのリンクをクリックすると、フォーム画面が表示されるようになっていることを確認します。

入力して登録ボタンを押すと、招待する側と同一の family_id で登録されました。
登録後のプロフィール設定画面、家族設定画面は、以下の通りです。


phpMyAdmin での users テーブルのデータの確認画面です。

最後の行の id 5 が追加されたデータです。
おわりに
実装したオリジナルの機能の一つである「家族ユーザー招待」機能についてはこれで完結です。記事にする過程で、1年ほど触っていない Laravel の実装について、思い出すことができたので良い復習の機会になりました。理解の不足についても少し穴埋めできたと感じています。
ありがとうございました。
これまでの関連記事
これまでの関連記事です。
100いいね、100ありがとうございます!4カ月で4記事が100いいね以上いただいてました!...ありがたい🙏🙏🙏🙏 https://t.co/qFhRyrIFuq
— やまて|Webエンジニア2年目 (@r_yamate) 2022年11月4日
スマレジ入社時研修当時のレビューを出しにして、たくさんのいいねを獲得しました。