転職したらスマレジだった件

スマレジのエンジニアやまてのテックブログです。マジレス大歓迎です。

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

こんにちは!

スマレジ・テックファームのWebエンジニアやまてと申します。

はじめに

今回は、Web業界実務未経験での転職活動用にポートフォリオとして作成した『はじめてのWebアプリ開発を振り返る』記事です。


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

qiita.com


実装したオリジナルの機能の一つである「家族ユーザー招待」機能について、記事にします。

概要と基本設計

ryamate.hatenablog.com

詳細設計と実装

(1) 家族招待メール送信フォームの作成

(2) マイグレーションファイルからのテーブル作成

(3) 家族招待メール(テキスト版)の送信処理の作成

(4) 家族招待メール(テキスト版)のテンプレートの作成

(5) ユーザー登録フォームと登録処理の作成【今回】



今回は、家族招待用の新規登録フォーム画面表示処理、家族ユーザー登録処理の作成手順です。

「家族招待用の登録フォーム画面表示」処理 & 「家族招待用のユーザー登録」処理


目次

使用技術、サービスなど

  • フロントエンド
  • バックエンド
    • PHP 7.4.13
    • Laravel 6.20.20
    • MySQL 8.0.23
  • メール関連
    • MailHog(開発者向けのメールテストツール、開発環境)
    • SendGrid(メール配信サービス、本番環境)


1. ルーティングの確認

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

ryamate.hatenablog.com

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

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

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

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.phpregisterInvitedUser メソッドを追加します。

  • 編集: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 の実装について、思い出すことができたので良い復習の機会になりました。理解の不足についても少し穴埋めできたと感じています。

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



これまでの関連記事

これまでの関連記事です。

ryamate.hatenablog.com

ryamate.hatenablog.com

ryamate.hatenablog.com



スマレジ入社時研修当時のレビューを出しにして、たくさんのいいねを獲得しました。