Friction River Software

  • お問い合わせ

CakePHP5入門【CakePHP5実用編⑩】ユーザ登録申込み②

A子

それじゃ、お試しユーザ自動登録機能を実装していこう

とりあえずは、申込者情報を格納するためのデータベーステーブルの設計からだね

C菜

テーブル名は「applicants」にしましょう~

create table applicants (
    id int auto_increment primary key,
    email varchar(255),
    password varchar(255),
    handle_name varchar(255),
    created datetime,
    modified datetime
) charset=utf8mb4;

基本的には「users」テーブルの流用ですね~
これ以外に必要な項目って何でしょうか~?

A子

URLの末尾にくっつけるランダムな文字列が要るよね

あと、有効期限(datetime型)も格納しないと…

B美

登録完了か、未登録かのフラグ値を入れる項目も必要かもね
(無くても問題は無いけど…)

C菜

まとめると、こんな感じでしょうか~?

create table applicants (
    id int auto_increment primary key,
    email varchar(255),
    password varchar(255),
    handle_name varchar(255),
    token varchar(255),
    deadline datetime,
    regist_flag int,

    created datetime,
    modified datetime
) charset=utf8mb4;

token」がランダム文字列で、「deadline」が有効期限、「regist_flag」が登録・未登録フラグです~

A子

うん、良いと思う
んじゃ、mysqlクライアントでテーブル定義を追加しよう

mysql -u root -p chatdb[Enter]

さっきの「CREATE TABLE」文をコピペして[Enter]っと…

C菜

では、このテーブル定義を元に「bake all」しましょう~

cd html/authapp[Enter]
bin/cake bake all applicants[Enter]



A子

まずは(事前準備として)不要なコードを削ったり、要らないファイルを削除しよう

「src/Controller」の中に作られた「ApplicantsController.php」については、「index」メソッドだけを残して他のメソッドは全て削除ね

C菜

templates/Applicants」の中にできた四つのViewビューファイルも削除して大丈夫ですよね~?

このあと、以前作った「templates/Users」の「first_user.php」を「templates/Applicants」の中にコピーして、ファイル名を「index.php」に変更しましょう~
(流用できるものは流用するです~)

A子

そのファイル(index.php)の変更点だけど、それほど多くはないね

一番上にログイン画面へのリンクを追加したのと、フォームの送信先を変えたくらいかな
(送信先については、「確認画面」という意味合いで「confirm」にしたよ)



B美

ApplicantsController.php」の中の「confirm」メソッドだけど、私のほうで作ってみたわ

public function confirm()
{
    $success_flag = false;
    $email = '';
    $password = '';
    $handle_name = '';
    $email_error_msg = '';
    $password_error_msg = '';
    $handle_name_error_msg = '';

    if ($this->request->is('post')) {
        $email = $this->request->getData('email');
        $password = $this->request->getData('password');
        $handle_name = $this->request->getData('handle_name');

        //ユーザIDの空文字列及び重複チェック
        if ($email != '') {
            if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
                if ($this->Users->find()->where(['email' => $email])->count() > 0) {
                    $email_error_msg = 'そのユーザIDはすでに登録済みです。';
                }
            } else {
                $email_error_msg = '不正なメールアドレス形式です。';
            }
        } else {
            $email_error_msg = 'ユーザIDが未入力です。';
        }

        //パスワードの空文字列チェック
        if ($password == '') {
            $password_error_msg = 'パスワードが未入力です。';
        }

        //ハンドルネームの空文字列及び重複チェック
        if ($handle_name != '') {
            if ($this->Users->find()->where(['handle_name' => $handle_name])->count() > 0) {
                $handle_name_error_msg = 'そのハンドルネームはすでに登録済みです。';
            }
        } else {
            $handle_name_error_msg = 'ハンドルネームが未入力です。';
        }

        if ($email_error_msg == '' && $password_error_msg == '' && $handle_name_error_msg == '') {
            $success_flag = true;
        }
    }

    $this->set(compact('success_flag'));
    $this->set(compact('email'));
    $this->set(compact('password'));
    $this->set(compact('handle_name'));
    $this->set(compact('email_error_msg'));
    $this->set(compact('password_error_msg'));
    $this->set(compact('handle_name_error_msg'));
}

基本的に、入力値チェックをしてるだけなんだけどね


A子

ん?
Usersを使ってるってことは、テーブルのフェッチが必要なんじゃないの?

private $Users;

public function initialize(): void
{
    parent::initialize();

    $this->Users = $this->fetchTable('Users');
}

さらに、ログイン認証を不要にするための記述も…

public function beforeFilter(\Cake\Event\EventInterface $event)
{
    parent::beforeFilter($event);

    $this->Authentication->addUnauthenticatedActions(['index', 'confirm']);
}

上記の「addUnauthenticatedActions」メソッドに渡す引数だけど、とりあえずは「index」と「confirm」メソッドのみ指定してるよ

B美

大正解!
(なかなかやるわね)

それじゃ、次の工程よ
「templates/Applicants」の中に「confirm.php」を作ってみなさい

C菜

「templates/Users」の中にある「view.php」を「templates/Applicants」にコピーしたあと、そのファイルを「confirm.php」にリネームするです~

その中身を書き換えて…っと、こんな感じでしょうか~?
あ、フォームの送信先は「regist」メソッドにしていますよ~

A子

うーん、良い感じなんだけど…
入力画面へ戻る」をクリックしたら、入力欄がクリアされちゃうのよねー

これって、なんとかならない?
(要するに、入力した値を復元したいってこと)

B美

考え方は「regist」へのフォーム送信と同じよ

送信先を「index」にすれば良いってこと

C菜

わかりました~

これでどうでしょうか~?


A子

あ、もちろん「ApplicantsController.php」の「index」メソッドも書き換えるよ

public function index()
{
    $email = '';
    $password = '';
    $handle_name = '';

    //確認画面から戻った際、hiddenデータを使って入力欄を復元する
    if ($this->request->is('post')) {
        $email = $this->request->getData('email');
        $password = $this->request->getData('password');
        $handle_name = $this->request->getData('handle_name');
    }

    $this->set(compact('email'));
    $this->set(compact('password'));
    $this->set(compact('handle_name'));
}

C菜

バッチリです~

それでは次に「regist」メソッドですね~
ここでは、ランダム文字列を作ってメール送信すると共に、「applicants」テーブルに登録するです~

A子

えっとー、処理の流れはこんな感じかな?

1.フォーム送信された三つのパラメータを受け取る
2.Applicantsレコードを新規作成
    2-1.「email」に「ユーザID」を格納
    2-2.「password」に「パスワード」を格納
    2-3.「handle_name」に「ハンドルネーム」を格納
3.「token」に格納する値は、以前作ったランダム文字列生成関数で作る
4.現時点から一時間後の時間を作成して、「deadline」に入れる
5.「regist_flag」には「1」を入れて、ユーザ登録後は「2」に変更する
6.入力された「ユーザID」あてにメール送信を行う

…って流れでどうかな?
(ランダム文字列生成関数については【CakePHP5応用編⑩】を参照)

B美

あ、一点気になることが…

2-2のパスワードだけど、平文ひらぶんでそのまま格納しておくの?

A子

あー、やっぱマズいかな?

Usersみたいにハッシュ化したほうが良い?

B美

うーん、そのほうが無難でしょうね
てか、【CakePHP5認証編①】の最後にやったよね?

use Authentication\PasswordHasher\DefaultPasswordHasher;

を先頭に書いて

(new DefaultPasswordHasher())->hash($password)

で、ハッシュ値が取得できるって…

C菜

あ~、ありましたね~

たしか「src/Model/Entity」の中にある「User.php」だったと思います~

A子

まじか、C菜
なんで憶えてるのよ(苦笑)

まぁ、それはともかく…
メール送信の準備から始めていこう

MATE端末」を開いて

cd html/authapp[Enter]
bin/cake bake mailer applicant[Enter]

を実行して、「src/Mailer」の中に「ApplicantMailer.php」を作るよ

C菜

えっと~

とりあえず「config」の中にある「app.php」を修正しましょう~
(「className」を「'Smtp'」にするだけですけど~)

A子

んじゃ、次は「html/bbsapp/src/Mailer」の中にある「PostMailer.php」を参考にして、「ApplicantMailer.php」を書き換えよう

public function regist($email, $url)
{
    $this->setEmailFormat('html')
        ->setFrom([FROM_ADDRESS => APPLICATION_NAME])
        ->setTo($email)
        ->setSubject(MAIL_REGIST_SUBJECT)
        ->viewBuilder()
            ->setTemplate('regist')
            ->setLayout('default')
            ->setVars(['url' => $url]);
}

もちろん定数定義もしておくよ(「const.php」内に追加)

define("FROM_ADDRESS", "root@friction-river.jp");    //送信元メールアドレス
define("MAIL_REGIST_SUBJECT", "お試しユーザ登録確認メール");    //お試しユーザ登録確認メールのタイトル
define("MAIL_URL", "https://192.168.1.205/authapp/applicants/welcome/");    //メール中に記載するURLの中でトークン以外の部分

ちなみに、「MAIL_URL」の中にある「welcome」だけど、このメソッドをあとで「ApplicantsController.php」内に記述する予定だから…

あとは、「html/bbsapp/templates/email/html」の中の「add.php」を「html/authapp/templates/email/html」にコピペして、「regist.php」にリネームだね






B美

ふむ、良いんじゃない?

それじゃ、ここから「ApplicantsController.php」への「regist」メソッドの実装ね
とりあえずは、できるところまでやってみなさい

A子

こんな感じでどうかな?

まずは先頭に以下のuse文を追加

use Authentication\PasswordHasher\DefaultPasswordHasher;
use Cake\Mailer\MailerAwareTrait;

あ、クラスの先頭にもuse文を追加ね

class ApplicantsController extends AppController
{
    use MailerAwareTrait;
    ・・・

んで、「regist」メソッドがこんな感じ…

public function regist()
{
    $success_flag = false;

    if ($this->request->is('post')) {
        $email = $this->request->getData('email');
        $password = $this->request->getData('password');
        $handle_name = $this->request->getData('handle_name');

        //ランダム文字列(20文字)の生成
        $token = substr(bin2hex(random_bytes(20)), 0, 20);

        //データベース登録
        $applicant = $this->Applicants->newEmptyEntity();
        $applicant->email = $email;    //メールアドレス
        $applicant->password = (new DefaultPasswordHasher())->hash($password);    //パスワード(ハッシュ値)
        $applicant->handle_name = $handle_name;    //ハンドルネーム
        $applicant->token = $token;    //URLトークン
        $applicant->deadline = date("Y-m-d H:i:s", strtotime("+1 hour"));    //有効期限(1時間後)
        $applicant->regist_flag = 1;    //登録状態(1…申込み直後,2…ユーザ登録済み)

        if ($this->Applicants->save($applicant)) {
            $this->getMailer('Applicant')->send('regist', [$email, MAIL_URL.$token]);    //メール送信
            $success_flag = true;
        }
    }

    $this->set(compact('success_flag'));
}

あ、「beforeFilter」メソッドには、'regist''welcome'を追加しといたよ


B美

むむ、A子のくせになかなかやるわね(苦笑)

現時点では、特に言うべきことは無いかな

C菜

A子社長、すごいです~

A子

C菜は純粋な賛辞を贈ってくれるのに、B美ときたら…(苦笑)
まぁ良いけどさ

あとは、結果表示のために「templates/Users」の中の「view.php」を「templates/Applicants」にコピペしてから「regist.php」にリネームしよう

C菜

あとは、ログインページ(login.php)上に「お試しユーザ登録」へのリンクを作りますね~



B美

ここまでの段階で、一度テストしてみなさい

「applicants」テーブルへの登録と、システムから送られたメールをきちんと受信できるかどうか…ってことね







A子

データベース登録も確認できたし、メールも届いたよ
(有効期限も一時間後になってたし…)

ここまでは完璧ね

B美

OK
それじゃ、今回はここまでにしておきましょう

次回がいよいよ「ユーザ自動登録」の最終回ね

C菜

了解です~

A子

頭を使い過ぎて、疲労困憊だよ(苦笑)