【Laravel6】ユーザー招待機能の詳細設計と実装(5)ユーザー登録フォームと登録処理

# **はじめに**

ユーザー招待機能の詳細設計と実装(5) ユーザー登録フォームと登録処理 について投稿します。

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

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

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

## 投稿内容（全5回）

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

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

今回は、**ユーザー招待機能の詳細設計と実装(5) ユーザー登録フォームと登録処理** についてです。 

## 目次

以下、本記事の目次です。
1. [ルーティングの確認](#1-ルーティングの確認)
1. [「家族招待用の登録フォーム表示」処理の追加](#2-家族招待用の登録フォーム表示処理の追加)
1. [家族招待用の登録フォームの作成](#3-家族招待用の登録フォームの作成)
1. [「家族ユーザー登録」処理の追加](#4-家族ユーザー登録処理の追加)
1. [家族招待用の登録フォームの確認](#5-家族招待用の登録フォームの確認)

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

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

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

ルーティングの追加は、以下のステップで完了しています。

https://qiita.com/ryamate/items/a1d0a5b1db9d11a69671#1-%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%AE%E8%BF%BD%E5%8A%A0

# 2. 「家族招待用の登録フォーム表示」処理の追加

**app/Http/Controllers/Auth** ディレクトリの **RegisterController.php** に `showInvitedUserRegistrationForm` メソッドを追加します。

```php:編集：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 エラーを返すようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/b9d20d3f-f519-a1d2-1f0e-d224ad62a42a.png)

# 3. 家族招待用の登録フォームの作成

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/7da1ff4c-e052-6d4e-3106-95311a2ea840.png)

```php:作成・編集：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` メソッドを追加します。

```php:編集：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 の受信ボックスに届いているメールのリンクをクリックすると、フォームが表示されるようになっていることを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/91ef2e1c-d22c-d4ef-fd25-99536fc857ef.png)

入力して登録ボタンを押すと、招待する側と同一の `family_id` で登録されました。

登録後のプロフィール設定画面、家族設定画面は、以下の通りです。

- プロフィール設定画面
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/b8e4df35-fc70-bb68-b28c-6b8a87ce88f7.png)

    
- 家族設定画面
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/732e61a1-4e6f-00af-9301-2bda79423d24.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527226/a2d8427f-2ad9-9c1a-b925-d8238c88b0c7.png)

最後の行の id 5 が追加されたデータです。

# おわりに

実装したオリジナルの機能の一つである「家族ユーザー招待」機能についてはこれで完結です。

記事にまとめる目的は、類似の機能を作る方への参考にしていただきたいため、自身の学習の振り返りのため、としていましたが、記事にする過程で、1年ほど触っていない Laravel の実装について、思い出すことができたので良い復習の機会になりました。

また、類似の機能を作る方への参考になると嬉しいです。

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