前回はログイン画面と新規ユーザー登録画面で入力チェックをリアルタイムで行う処理の追加を行いました。
今回は、ログイン画面に「パスワードをお忘れの場合」というリンクを追加し、パスワードを忘れてしまったユーザーがパスワードを再設定できる機能を実装します。
パスワード再設定機能
パスワード再設定機能の開発については以下の記事を参考にさせていただきました。
パスワード再設定機能は以下の9ステップで進めていきます。
ステップ数が多いので今記事ではステップ1からステップ5までを進めます。
1 ニックネーム登録プログラムをメールアドレス登録プログラムに書き換え(DB構造も修正)
2 ローカル環境でメールを送信する設定
3 リセットディレクトリとファイルの作成
4 ログイン画面に再設定リンク設置
5 リセットリクエストを記録するDBテーブルを作成
(以下後編)
6 認証とメール送信ページ実装
7 送信完了ページ実装
8 パスワード再設定ページ実装
9 パスワード再設定完了ページ実装
ニックネーム登録プログラムをメールアドレス登録プログラムに書き換え
パスワード再設定はユーザーが登録しているメールアドレスに再設定用のURLを送信するため、ユーザー情報にメールアドレスが追加で必要になります。
ユーザー情報項目として追加してもよいのですが、今回はここまで使う場面のなかったニックネームをメールアドレスに変更します。
ニックネームが関連するプログラムを記述しているファイルは以下の6ファイルです。
・join/indes.php
・join/confirm.php
・login.php
・session.php
・account.php
・account-update.php
上記ファイル内のnicknameをemailに書き換えていきます。
以下のプログラムはnicknameがある部分を抜粋して記載しています。
- ファイル内検索置換でnicknameと検索しヒットした12件の文字列をemailに置換
- 「//フォームデータ格納」の$emailの第三引数を「FILTER_SANITIZE_EMAIL」に修正
- ニックネームを入力するinputの「maxlength=12」を削除
- 同上inputのonkeyup属性のnicknameCheck();を「emailCheck();」に修正
- 同上inputに以下pattern属性を追加
 pattern=”^[a-zA-Z0-9.!#$&’*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$”
- ニックネームエラーメッセージ表示要素のid属性のnicknameCheckを「emailCheck」に修正
- <p>ニックネーム<span class=”c-text–red”>※必須</span><span>※12文字以内</span></p>
 を
 <p>メールアドレス<span class=”c-text–red”>※必須</span></p>
 に修正
//修正がある部分抜粋
//書き直しモードでセッション内容を復元
if (isset($_GET['mode']) && $_GET['mode'] === 'modify' && isset($_SESSION['email'], $_SESSION['username'], $_SESSION['password'], $_SESSION['initial_savings'])) :
  $email = $_SESSION['email'];
  $username = $_SESSION['username'];
  $password = $_SESSION['password'];
  $initial_savings = $_SESSION['initial_savings'];
else :
  $email = '';
  $username = '';
  $password = '';
  $initial_savings = '';
endif;
if ($_SERVER['REQUEST_METHOD'] === 'POST') :
  //フォームデータ格納
  $email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
  $username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_SPECIAL_CHARS);
  $password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_SPECIAL_CHARS);
  $password_confirm = filter_input(INPUT_POST, 'password_confirm', FILTER_SANITIZE_SPECIAL_CHARS);
  $initial_savings = filter_input(INPUT_POST, 'initial_savings', FILTER_SANITIZE_SPECIAL_CHARS);
  //ユーザーネーム重複確認
  $sql = 'SELECT COUNT(*) FROM user WHERE username = ?';
  $stmt = $db->prepare($sql);
  $stmt->bind_param('s', $username);
  sql_check($stmt, $db);
  $stmt->bind_result($count);
  $stmt->fetch();
  if ($count === 0) :
    $exist = 'notexist';
  else :
    $exist = 'exist';
  endif;
  //パスワード一致確認
  if ($password !== $password_confirm) :
    $password_match = 'notmatch';
  else :
    $password_match = 'match';
  endif;
  //ユーザー名とパスワードが異なる文字列か
  if ($username === $password) :
    $samestr = "same";
  else :
    $samestr = "different";
  endif;
endif;
//確認画面へ遷移
if ($exist === 'notexist' && $password_match === 'match' && $samestr === "different") :
  $_SESSION['email'] = $email;
  $_SESSION['username'] = $username;
  $_SESSION['password'] = $password;
  $_SESSION['initial_savings'] = $initial_savings;
  header('Location: confirm.php');
  exit();
endif;
$page_title = '新規ユーザー登録';
include_once('./header.php');
?>
<main class="l-main">
  <?php if ($exist === 'exist' || $password_match === 'notmatch' || $samestr === 'same') : ?>
    <section class="p-section p-section__message p-section__message--join">
      <div class="p-message-box p-message-box--error">
        <?php if ($exist === 'exist') : ?>
          <p>すでに登録されているユーザー名です。</p>
        <?php endif; ?>
        <?php if ($password_match === 'notmatch') : ?>
          <p>パスワードが一致しません。</p>
        <?php endif; ?>
        <?php if ($samestr === 'same') : ?>
          <p>ユーザー名とパスワードは異なる文字列を入力してください。</p>
        <?php endif; ?>
      </div>
    </section>
  <?php endif; ?>
  <section class="p-section p-section__join-input">
    <form class="p-form p-form--join" action="" method="post">
      <div class="p-form__vertical-input">
        <p>メールアドレス<span class="c-text--red">※必須</span></p>
        <input type="text" name="email" autocomplete="off" id="email" class="js-check" value="<?php echo h($email); ?>" onkeyup="emailChange();inputCheck('join');" pattern="^[a-zA-Z0-9.!#$&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$" required>
        <p class="email-check" id="emailCheck"></p>
      </div>
<!--以下省略-->- ファイル内検索でnickname文字列7件をemailに置換
- <p>ニックネーム</p> を <p>メールアドレス</p> に修正
//修正がある部分抜粋
if (isset($_SESSION['email']) && isset($_SESSION['username']) && isset($_SESSION['password']) && isset($_SESSION['initial_savings'])) :
  $email = $_SESSION['email'];
  $username = $_SESSION['username'];
  $password = $_SESSION['password'];
  $initial_savings = $_SESSION['initial_savings'];
else :
  header('Location: index.php');
  exit();
endif;
//登録ボタン押下でデータを登録
if ($_SERVER['REQUEST_METHOD'] === 'POST') :
  $sql = 'INSERT INTO user(email, username, password, initial_savings) VALUES(?, ?, ?, ?)';
  $stmt = $db->prepare($sql);
  $encryption = password_hash($password, PASSWORD_DEFAULT);
  $stmt->bind_param('sssi', $email, $username, $encryption, $initial_savings);
  sql_check($stmt, $db);
  unset($_SESSION['email'], $_SESSION['username'], $_SESSION['password'], $_SESSION['initial_savings']);
  $_SESSION['login_times'] = 'first';
  header('Location: thanks.php');
endif;
$page_title = '登録情報確認';
include_once('./header.php');
?>
<main class="l-main">
  <section class="p-section p-section__join-confirm">
    <form class="p-form p-form--join" action="" method="post">
      <div class="p-form__vertical-input">
        <p>メールアドレス</p>
        <p>【<?php echo h($email); ?>】</p>
<!--以下省略-->- ファイル内検索でnickname文字列3件をemailに置換
//修正がある部分抜粋
//ログインボタン押下処理
if ($_SERVER['REQUEST_METHOD'] === 'POST') :
  $post_username = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_SPECIAL_CHARS);
  $post_password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_SPECIAL_CHARS);
  //ログイン確認
  $sql = 'SELECT * FROM user WHERE username = ? LIMIT 1';
  $stmt = $db->prepare($sql);
  $stmt->bind_param('s', $post_username);
  sql_check($stmt, $db);
  $stmt->bind_result($user_id, $email, $username, $hash_password, $initial_savings);
  $stmt->fetch();
  //パスワード一致確認処理
  if (password_verify($post_password, $hash_password)) : //ログイン成功時
    session_regenerate_id();
    $_SESSION['user_id'] = $user_id;
    $_SESSION['email'] = $email;
    $_SESSION['username'] = $username;
    $_SESSION['initial_savings'] = $initial_savings;
    //初期ログインかそれ以外で処理を可変
    if (isset($_POST['to_login'])) :
      header('Location: ./index.php');
      exit();
    elseif (isset($_POST['to_setting'])) :
      header('Location: ./item-edit.php?editItem=0');
      exit();
    endif;
    exit();
  //ログイン失敗時
  else :
    $login = 'error';
  endif;
endif;
//以下省略- ファイル内検索でnicknameを3件emailに置換
//修正がある部分抜粋
if (isset($_SESSION['user_id']) && isset($_SESSION['email']) && isset($_SESSION['initial_savings']) && isset($_SESSION['username'])) :
  $user_id = $_SESSION['user_id'];
  $username = $_SESSION['username'];
  $email = $_SESSION['email'];
  $initial_savings = $_SESSION['initial_savings'];
else :
  header('Location: login.php');
  exit();
endif;- ファイル内検索でnickname10件をemailに置換
- ファイル内検索でニックネーム2件をメールアドレスに置換
- $caution = “※12文字以内”; を削除
- 新しいメールアドレスを入力するinputから「maxlength=12」を削除
- 同上inputに以下pattern属性を追加
 pattern=”^[a-zA-Z0-9.!#$&’*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$”
<!--修正がある部分抜粋-->
  <?php if (($_SERVER['REQUEST_METHOD'] === 'POST') && (isset($_POST['modify_email']) || isset($_POST['modify_username']) || isset($_POST['modify_password']) || isset($_POST['modify_initial_savings']))) : ?>
    <?php
    if (isset($_POST['modify_email'])) {
      $item_label = "メールアドレス";
      $column = "email";
      $caution = "※12文字以内";
      $modify_item = $_POST['email'];
    } elseif (isset($_POST['modify_username'])) {
      $item_label = "ユーザー名";
      $column = "username";
      $caution = "※半角英数字6〜12文字";
      $modify_item = $_POST['username'];
    } elseif ($_POST['modify_password']) {
      $item_label = "パスワード";
      $column = 'password';
      $caution = "※半角英数字6〜12文字";
      $modify_item = "非表示";
    } elseif (isset($_POST['modify_initial_savings'])) {
      $item_label = "初期貯蓄額";
      $column = "initial_savings";
      $modify_item = number_format($_POST['initial_savings']) . '円';
    }
    ?>
    <section class="p-section p-section__full-screen">
      <form class="p-form p-form--account-edit" action="./account-update.php" method="POST">
        <input type="hidden" name="column_value" value="<?php echo h($column); ?>">
        <div class="p-form__vertical-input">
          <p>現在の<?php echo h($item_label); ?></p>
          <?php if ($column === 'password') : ?>
            <input type="password" name="now_password" required>
          <?php else : ?>
            <p><?php echo h($modify_item); ?></p>
          <?php endif; ?>
        </div>
        <div class="p-form__vertical-input">
          <p>新しい<?php echo h($item_label) . h($caution); ?></p>
          <!-- どの情報の変更かでinputの入力条件を変える -->
          <?php if (isset($_POST['modify_email'])) : ?>
            <input type="text" name="modify_value" pattern="^[a-zA-Z0-9.!#$&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$" required>
          <?php elseif (isset($_POST['modify_username'])) : ?>
            <input type="text" name="modify_value" minlength="6" maxlength="12" pattern="^[0-9a-zA-Z]+$" required>
          <?php elseif (isset($_POST['modify_password'])) : ?>
            <input type="password" name="modify_value" minlength="6" maxlength="12" pattern="^[0-9a-zA-Z]+$" required>
          <?php elseif (isset($_POST['modify_initial_savings'])) : ?>
            <input type="number" name="modify_value" required>
          <?php endif; ?>
        </div>
        <div class="p-form__center">
          <a class="c-button" href="./account.php">キャンセル</a>
          <?php if ($column === 'password') : ?>
            <input class="c-button c-button--bg-blue" type="submit" name="password_modify" value="変更する">
          <?php else : ?>
            <input class="c-button c-button--bg-blue" type="submit" name="other_modify" value="変更する">
          <?php endif; ?>
        </div>
      </form>
    </section>
  <?php endif; ?>
  <section class="p-section">
    <h2 class="c-text c-text__subtitle">【アカウント管理】</h2>
    <form class="p-form p-form--account" action="" method="POST">
      <div class="info">
        <p>メールアドレス</p>
        <input type="hidden" name="email" id="email" value="<?php echo h($email); ?>">
        <p><?php echo h($email); ?></p>- ファイル内検索でnickname2件をemailに置換
//修正がある部分抜粋
//セッション修正
if ($column_value === "nickname") :
  $_SESSION['nickname'] = $modify_value;ファイルの書き換えができたので続けてデータベースの構造を変更します。
以上でステップ1は完了です。
ローカル環境でメールを送信する設定
これからパスワード再設定の機能を実装するにあたり、ローカル環境からメールを送信できる設定を行う必要があります。
今記事では設定方法の説明は割愛します。
筆者は以下の記事を参考に設定しましたので、参考にしてください。

リセットディレクトリとファイルの作成
続いてパスワード再設定で使用するファイルを作成します。
再設定用のファイルは、password-resetというディレクトリの中に格納していきます。

各ファイル内の実装は後編で進めていきます。
ログイン画面に再設定リンク設置
login.phpに先ほど作成したpassword-reset/index.phpに飛ばすリンクを設置します。
form閉じタグの下に再設定ページへ飛ばすaタグを追加します。
</form>
<!--↓↓追加↓↓-->
<a href="./password-reset/index.php" class="c-link">パスワードをお忘れの場合</a>CSSはすでに設定済みなので、以上を追記すると以下のような再設定に飛ばすリンクが表示されます。

以上でパスワード再設定のリンク設置は完了です。
リセットリクエストを記録するDBテーブルを作成
続いてリセットリクエストの記録を保存するデータベーステーブルを新規作成します。
一般的なパスワード再設定で見られる、リクエストがあった際にランダムな文字列をパラメータにつけてユーザーにメールを送るという処理をこのシステムでも使うので、リクエストを送ったユーザーのユーザー名とメールアドレス、そしてランダムな文字列(token)とリクエスト日時を保存するテーブルを作成します。
テーブルの構造は以下のとおりです。

最後に
今回からはパスワード再設定機能の実装を進めています。
前編では、既存プログラムやテーブルカラムの修正やメール送信設定、ファイルやテーブルの作成といった下準備を行いました。
次回からは作成したファイルに再設定の機能を実装していきます。
最後までお読みいただきありがとうございました。







コメント