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

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

C菜
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子
あと、有効期限(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菜
cd html/authapp[Enter]
bin/cake bake all applicants[Enter] |




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


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


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




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


A子
「入力画面へ戻る」をクリックしたら、入力欄がクリアされちゃうのよねー
これって、なんとかならない?
(要するに、入力した値を復元したいってこと)

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

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



A子
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子
なんで憶えてるのよ(苦笑)
まぁ、それはともかく…
メール送信の準備から始めていこう
「MATE端末」を開いて
cd html/authapp[Enter]
bin/cake bake mailer applicant[Enter] |
を実行して、「src/Mailer」の中に「ApplicantMailer.php」を作るよ


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


A子
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美
現時点では、特に言うべきことは無いかな

C菜

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


C菜




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

↓

↓

↓


A子
(有効期限も一時間後になってたし…)
ここまでは完璧ね


B美
それじゃ、今回はここまでにしておきましょう
次回がいよいよ「ユーザ自動登録」の最終回ね

C菜

A子