7-7:会員機能を追加する

2019/05/29

概要

ECサイトに会員機能を追加していきます。
商品購入手続きのときに、会員ログインしていたら、登録の氏名・メールアドレス・住所が動的に入力フォームに反映されるようにします。
また、会員特典として、商品の購入金額が自動的に30%オフになるように実装していきます。

フォルダ階層

完成イメージ

ログイン

カート(ログイン:30%オフ表示)

カート(未ログイン)

購入者情報(ログイン:会員情報反映)

全体の手順

手順は以下のとおりです

  1. ログイン画面作成
  2. ログイン機能実装
  3. 購入画面で会員データ反映
  4. 確認画面で購入金額30% オフ実装
  5. ログインチェック機能の反映

ログイン画面の作成

login.php作成

login.phpを作成します。
register.htmlをコピーして、titleを「ログイン」に変更します。
<div class="container">内を削除します。
login.php

<!DOCTYPE html>
<html>

<head>
    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-13xxxxxxxxx"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());

        gtag('config', 'UA-13xxxxxxxxx');
    </script>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>ログイン|SQUARE, inc.</title>

    <link rel="icon" href="favicon.ico">

    <!-- css -->
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="responsive.css">

    <!-- icon -->
    <link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">

</head>

<body>
    <header>
        <div class="container">
            <div class="header-logo">
                <h1><a href="index.php"><img src="img/square_logo.png" id="logo"></a></h1>
            </div>

            <!-- ハンバーガーメニューボタン -->
            <div class="toggle">
                <div>
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>

            <div class="cart">
                <a href="cart.php"><i class="fas fa-shopping-cart"></i></a>
            </div>

            <nav class="sp-menu menu">
                <ul>
                    <li><a href="index.php#service">サービス</a></li>
                    <li><a href="shop.php">商品一覧</a></li>
                    <li><a href="index.php#news">お知らせ</a></li>
                    <li><a href="index.php#about">会社概要</a></li>
                    <li><a href="index.php#contact">お問合せ</a></li>
                    <li><a href="ブログのURL">ブログ</a></li>
                    <li><a href="register.html">会員登録</a></li>
                </ul>
            </nav>

            <nav class="pc-menu menu-left menu">
                <ul>
                    <li><a href="index.php#service">サービス</a></li>
                    <li><a href="shop.php">商品一覧</a></li>
                    <li><a href="index.php#news">お知らせ</a></li>
                    <li><a href="index.php#about">会社概要</a></li>
                    <li><a href="index.php#contact">お問合せ</a></li>
                    <li><a href="ブログのURL">ブログ</a></li>
                </ul>
            </nav>
            <nav class="pc-menu menu-right menu">
                <ul>
                    <li><a href="cart.php"><i class="fas fa-shopping-cart"></i></a></li>
                    <li><a href="register.html">会員登録</a></li>
                </ul>
            </nav>
        </div>
    </header>
    <main>
        <!-- register -->
        <div class="wrapper last-wrapper register-wrapper">
            <div class="container">

            </div>
        </div>
    </main>
    <footer>
        <div class="container">
            <p>Copyright @ 2018 SQUARE, inc.</p>
        </div>
    </footer>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <script>

        $(function () {
            // ハンバーガーメニューの動作
            $('.toggle').click(function () {
                $("header").toggleClass('open');
                $(".sp-menu").slideToggle(500);
            });

        });

    </script>
</body>

</html>

ログインに必要なフォームを追加します。
必要なフォームはmailとpasswordです。
会員登録が済んでない人用に、会員登録への誘導ボタンをつけています。

login.php

        <main>
            <!-- register -->
            <div class="wrapper last-wrapper register-wrapper">
                <div class="container">
+                   <div class="login">
+                       <div class="wrapper-title">
+                           <h4>ログイン</h4>
+                       </div>
+                       <form class="login-form">
+                           <div class="form-group">
+                               <p>メールアドレス</p>
+                               <input type="email" name="email" required>
+                           </div>
+                           <div class="form-group">
+                               <p>パスワード</p>
+                               <input type="password" name="password" required>
+                           </div>
+                           <button type="submit" class="btn btn-gray">ログイン</button>
+                       </form>
+                   </div>
+                   <div class="register">
+                       <div class="wrapper-title">
+                           <h4>新規登録</h4>
+                           <p>登録がお済みでない方はこちらから新規登録してください。</p>
+                       </div>
+                       <button type="button" class="btn btn-submit" onclick="location.href='./register.html'">新規登録</button>
+                   </div>
                </div>
            </div>
        </main>

CSSは以前、basic6-1 入会画面を作成するでregister.htmlを作成した際に追記したので、CSSは追加しません。

corporate-site/login.phpにアクセスします。
このようなログイン画面ができあがります。

ログインフォームのactionはuser_check.php、methodはPOSTを指定します。
(この時点では、user_chcek.phpはまだありません。)

login.php

+   <form class="login-form" action="user_check.php" method="POST">
-   <form class="login-form">

チェック機能作成

ユーザーのログインチェックをuser_check.phpで実装していきます。
user_check.phpというファイルを作成します。
管理画面の管理者ログインチェックではスタティックにメールアドレスとパスワードを定義して(admin@admin.comとpassword01)、ログイン可否をチェックしました。
今回は、usersテーブルを参照してユーザーのemailとpasswordが登録されているか調べてログインチェックをします。

データ受け取り

login.phpから送られてくるデータを取得します。

user_check.php

<?php

    $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
    $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

最低限エラーチェック

emailかpasswordどちらかでも空だった場合はlogin.phpにリダイレクトさせます。
(本来は、ここでパスワード・メールアドレスの文字数や正規表現などバリデーションチェックも入れますが割愛します。)

user_check.php

    <?php

        $email = isset($_POST['email'])? htmlspecialchars($_POST['email'], ENT_QUOTES, 'utf-8') : '';
        $password = isset($_POST['password'])? htmlspecialchars($_POST['password'], ENT_QUOTES, 'utf-8'): '';

+      if($email==''|$password==''){
+          header('location:./login.php');
+      }

DB接続

user_check.php


        if($email==''|$password==''){
            header('location:./login.php');
        }

+      //DB接続
+      try{
+          $dbh = new PDO("mysql:host=localhost;dbname=corporate_db","root","root");
+      }catch(PDOException $e){
+          var_dump($e->getMessage());
+          exit;
+      }

usersテーブル参照

取得した$email$passwordでusersテーブルを参照します。
user_check.php

    //DB接続
    try{
        $dbh = new PDO("mysql:host=localhost;dbname=corporate_db","root","root");
    }catch(PDOException $e){
        var_dump($e->getMessage());
        exit;
    }

+   $stmt = $dbh->prepare("SELECT * FROM users WHERE email=:email AND password=:password");
+   $stmt->bindParam(':email',$email);
+   $stmt->bindParam(':password',$password);
+   $stmt->execute();

該当チェック

該当のユーザーがいるかrowCount()を使ってチェックします。
もし、1レコードもない場合はlogin.phpにリダイレクトさせます。
user_check.php

    $stmt = $dbh->prepare("SELECT * FROM users WHERE email=:email AND password=:password");
    $stmt->bindParam(':email',$email);
    $stmt->bindParam(':password',$password);
    $stmt->execute();
+   $count = $stmt->rowCount();

+   if($count==0){
+       header('location:./login.php');
+   }else{
+   //ログインOK
+   }

ログインOKの処理

ログインがOKだった場合は、ログイン状態とユーザー情報をsessionで保持するようにします。
リダイレクト先は、トップページにします。
user_check.php

    if($count==0){
        header('location:./login.php');
    }else{
+       //ユーザー情報は$usersに配列で格納
+       $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

+      //session開始
+       session_start();
+       $_SESSION['user_login']=true;
+       $_SESSION['user_id']=$users[0]['id'];
+       $_SESSION['user_name']=$users[0]['name'];

+       //リダイレクト
+       header("Location:./index.php");
    }

+ ?>

ログアウト機能作成

ログイン機能を付けたのでログアウト機能も実装しておきます。
logout.phpを作成します。
user_loginをfalseにしてindex.phpにリダイレクトさせるだけです。
logout.php

<?php
    session_start();
    $_SESSION['user_login'] = false;

    header('location:./index.php');

確認

一度、login.phpにアクセスして、登録されているユーザーでログインしてみます。
登録したユーザーを忘れた場合は、コンソールで確認してください。

console

mysql> select name,email,password from users;

うまく実装できていれば、index.phpにリダイレクトされます。
ログインできている状態でlogout.phpにアクセスします。すると、ログアウト処理した後index.phpにリダイレクトさせるように実装したので、index.phpに戻ります。
ログアウトできているか、sessionを確認してみます。

$ cd /Applications/MAMP/tmp/php
$ cat sess_今日できたセッションファイル

結果に、user_login|b:0となっていればログアウトができています(0がログアウト、1がログインを表す)。
また、登録のないユーザーでログインしようとするとログイン画面にリダイレクトされることも確認してください。

購入者情報に反映

ここまでできたら、pay.phpの購入者情報にユーザーの情報を反映させていきます。
pay.phpの上部でログインのチェックをします。
管理画面のチェックとは異なり、ログインしている場合の処理を記述していきます。
(管理画面のログインチェックは、ログインできてなかったらリダイレクトさせる処理でした)
ログインしていたら、sessionからuser_idを取得します。
pay.php

<?php
    session_start();
    $user_login = isset($_SESSION['user_login'])? $_SESSION['user_login'] : false;
    if($user_login == true){

        //sessionからuser_id取得
        $user_id = $_SESSION['user_id']; 
    }else{
        //ログインしてない
    }
?>

取得したuser_idでデータベースを参照します。
pay.php

    <?php
        session_start();
        $user_login = isset($_SESSION['user_login'])? $_SESSION['user_login'] : false;
        if($user_login == true){

            //sessionからuser_id取得
            $user_id = $_SESSION['user_id']; 

+          //DB接続
+          try{
+              $dbh = new PDO("mysql:host=localhost;dbname=corporate_db","root","root");
+          }catch(PDOException $e){
+              var_dump($e->getMessage());
+              exit;
+          }
+          //user_idでSELECT文作成
+          $stmt = $dbh->prepare("SELECT * FROM users WHERE id=:id");
+          $stmt->bindParam(":id",$user_id);
+          $stmt->execute();
+          $users = $stmt->fetchAll(PDO::FETCH_ASSOC);

        }else{
            //ログインしてない
        }

usersテーブルで取得できる購入者情報に必要なデータは、name、email、addressです。
この3つを取得します。
また、ログインしていない場合は取得できないので、空をセットします。
pay.php

        //user_idでSELECT文作成
        $stmt = $dbh->prepare("SELECT * FROM users WHERE id=:id");
        $stmt->bindParam(":id",$user_id);
        $stmt->execute();
        $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
+       //データ取得
+       $name = $users[0]['name'];
+       $email = $users[0]['email'];
+       $address = $users[0]['address'];

    }else{
        //ログインしてない
+       $name = '';
+       $email = '';
+       $address = '';
    }

取得したデータをフォームで反映させます。
pay.php

    <form class="pay-form"  action="./pay_card.php" method="POST">
        <div class="form-group">
            <p class="form-title">お名前 *</p>
-          <input type="text" name="name" required>
+          <input type="text" name="name" required value="<?php echo $name; ?>">
        </div>
        <div class="form-group">
            <p class="form-title">Email *</p>
-          <input type="email" name="email" required >
+          <input type="email" name="email" required value="<?php echo $email; ?>">
        </div>
        <div class="form-group">
            <p class="form-title">電話番号 *</p>
            <input type="tel" name="tel" required>
        </div>
        <div class="form-group">
            <p class="form-title">お届け先 *</p>
            <label>郵便番号</label><br>
            <input type="text" name="postcode" required>
            <label>住所</label><br>
-          <input type="text" name="address" required>
+          <input type="text" name="address" required value="<?php echo $address; ?>">
        </div>
        <button type="submit" class="btn btn-blue">決済情報を入力する</button>
    </form>

login.phpからログインして、商品一覧にいき、pay.phpまで確認してみます。

データが反映されていればOKです。

確認画面で購入金額30% オフ実装

会員特典の30%オフ機能を実装します。

カート画面編集

合計金額はショッピングカートで表示されるので、cart.phpを編集していきます。

現在の合計金額が取得できたら、ログインチェックをして、ログインしてたら合計金額を30%オフにします。
ログインしていない人はそのままです。
cart.php

    <?php

        $delete_name = isset($_POST['delete_name'])? htmlspecialchars($_POST['delete_name'], ENT_QUOTES, 'utf-8') : '';

        session_start();

        if($delete_name != '') unset($_SESSION['products'][$delete_name]);

        $total = 0; 

        $products = isset($_SESSION['products'])? $_SESSION['products']:[];

        foreach($products as $name => $product){
            //各商品の小計を取得
            $subtotal = (int)$product['price']*(int)$product['count'];
            //各商品の小計を$totalに足す
            $total += $subtotal;
        }

+       $user_login = isset($_SESSION['user_login'])? $_SESSION['user_login'] : false;
+       if($user_login==true){
+           $total = $total*0.7;
+       }

HTMLで表示させます。
ログインしている場合は、赤字で「会員特別価格(30%OFF)」と表示させるようにしました。
cart.php

    <tbody>
        <?php foreach($products as $name => $product): ?>
        <tr>
            <td label="商品名:"><?php echo $name; ?></td>
            <td label="価格:" class="text-right">¥<?php echo $product['price']; ?></td>
            <td label="個数:" class="text-right"><?php echo $product['count']; ?></td>
            <td label="小計:" class="text-right">¥<?php echo $product['price']*$product['count']; ?></td>
            <td>
                <form action="cart.php" method="post">
                <input type="hidden" name="delete_name" value="<?php echo $name; ?>">
                <button type="submit" class="btn btn-red">削除</button>
                </form>
            </td>
        </tr>
        <?php endforeach; ?>
        <tr class="total">
            <th colspan="3">合計</th>
-           <td colspan="2">¥<?php echo $total; ?></td>
+           <td colspan="2">
+                <p><?php echo $total; ?>円</p>
+                <?php if($user_login == true) echo "<span style='color:red'>会員特別価格(30%OFF)</span>" ?>
+           </td>
        </tr>
    </tbody>

30%オフ金額も計算したので、totalもsessionに入れてしまいます。
cart.php

        $user_login = isset($_SESSION['user_login'])? $_SESSION['user_login'] : false;
        if($user_login==true){
            $total = $total*0.7;
        }

+       $_SESSION['total_price'] = $total;

    ?>

決済完了画面の編集

決済時に合計金額を再計算していましたが、sessionに合計金額を$total_priceという名前で保存したので、$total_priceを取得して決済を完了するようにpay_end.phpを編集します。

pay_end.php

    session_start();
    $products = isset($_SESSION['products'])? $_SESSION['products']:[];
+   $total = isset($_SESSION['total_price'])? $_SESSION['total_price']:0;
-   $total = 0;
-   foreach($products as $key => $product){
-       $subtotal = (int)$product['price']*(int)$product['count'];
-       $total += $subtotal;
-   }

    $currency = 'jpy';
    $res = \Payjp\Charge::create(array(
                "card" => $payjp_token,
                "amount" => (int)$total,
                "currency" => "jpy"
        ));

また、決済完了後、productsセッションと同じタイミングでtotal_priceのセッションもunsetします。
pay_end.php

        unset($_SESSION['products']);
+       unset($_SESSION['total_price']);
    ?>

画面を確認してみます。
商品一覧から確認してみます。
ログイン前は今までどおりです。

ログイン後カートを確認すると30%オフが適用されています。

これで、購入の流れ自体は完成しました。
決済完了画面まで動作確認をして会員情報や金額が反映されていればOKです。

ログインチェックを反映

カートページでログインチェック

購入時にログインを促すようにカートページのボタンを変更していきます。

ログインしていたら、pay.phpにそのまま進めるように「購入て手続きへ」ボタンを表示します。
ログインしていない場合は、login.phpに飛ぶ「ログインして購入手続きへ」ボタンと「ログインせずに購入手続きへ」ボタンを表示するようにします。
cart.php

    <div class="cart-btn">
+   <?php if($user_login==true): ?>
+       <!-- ログインしてる -->
        <button type="button" class="btn btn-blue" onclick="location.href='pay.php'" <?php if(empty($products)) echo 'disabled="disabled"'; ?>>購入手続きへ</button>
+   <?php else: ?>
+       <!-- ログインしていない -->
+       <button type="button" class="btn btn-blue" onclick="location.href='login.php'" <?php if(empty($products)) echo 'disabled="disabled"'; ?>>ログインして購入手続きへ</button>
+       <button type="button" class="btn btn-blue" onclick="location.href='pay.php'" <?php if(empty($products)) echo 'disabled="disabled"'; ?>>ログインぜずに購入手続きへ</button>
+   <?php endif; ?>
    <button type="button" class="btn btn-gray" onclick="location.href='shop.php'">お買い物を続ける</button>

    </div>

カート画面を確認します。
ログインしていないとボタンは3つ表示されます。

ログインしているとボタンは2つ表示されます。

これですべての流れができました。
商品を選んで、ログインして購入したり、ログインなしで購入したりして動作確認してください。
以上で会員機能は完成です。

メニューバー編集

メニューバーもログインできている状態のときは、「会員登録」ではなく「ログアウト」を表示させるうにします。
index.phpの例を記述しますが、すべてのページに適用させてください(変更したファイル名・リンク等2番を参照)。

まず、冒頭のphpの部分でsession_start()してuser_loginの状態を取得します。
index.php

    <?php

+       session_start();
+       $user_login = isset($_SESSION['user_login'])? $_SESSION['user_login']:false;

会員登録ボタンを表示させている部分にif文を追加します。
ログインしていれば、ログアウトを表示、そうでなければログインを表示するように変更しました。
レスポンシブ対応があるので、二箇所修正します。
こちらも、対象となるファイル全てに適用してください(変更したファイル名・リンク等3番を参照)。

index.php

    <header>
        <div class="container">
            <div class="header-logo">
                <h1><a href="index.php"><img src="img/square_logo.png" id="logo"></a></h1>
            </div>

            <!-- ハンバーガーメニューボタン -->
            <div class="toggle">
                <div>
                    <span></span>
                    <span></span>
                    <span></span>
                </div>
            </div>

            <div class="cart">
                <a href="cart.php"><i class="fas fa-shopping-cart"></i></a>
            </div>

            <nav class="sp-menu menu">
                <ul>
                    <li><a href="index.html#service">サービス</a></li>
                    <li><a href="index.html#news">お知らせ</a></li>
                    <li><a href="shop.php">商品一覧</a></li>
                    <li><a href="index.html#about">会社概要</a></li>
                    <li><a href="index.html#contact">お問合せ</a></li>
                    <li><a href="ブログのURL">ブログ</a></li>
-                   <li><a href="register.html">会員登録</a></li>
+                   <?php if($user_login==true): ?>
+                       <li><a href="logout.php">ログアウト</a></li>
+                   <?php else: ?>
+                       <li><a href="login.php">ログイン</a></li>
+                   <?php endif; ?>
                </ul>
            </nav>

            <nav class="pc-menu menu-left menu">
                <ul>
                    <li><a href="index.html#service">サービス</a></li>
                    <li><a href="index.html#news">お知らせ</a></li>
                    <li><a href="shop.php">商品一覧</a></li>
                    <li><a href="index.html#about">会社概要</a></li>
                    <li><a href="index.html#contact">お問合せ</a></li>
                    <li><a href="ブログのURL">ブログ</a></li>
                </ul>
            </nav>
            <nav class="pc-menu menu-right menu">
                <ul>
                    <li><a href="cart.php"><i class="fas fa-shopping-cart"></i></a></li>
-                  <li><a href="register.html">会員登録</a></li>
+                 <?php if($user_login==true): ?>
+                     <li><a href="logout.php">ログアウト</a></li>
+                 <?php else: ?>
+                     <li><a href="login.php">ログイン</a></li>
+                 <?php endif; ?>
            </nav>
        </div>
    </header>

また、register.htmlファイルも拡張子を変更してregister.phpにし、上記ヘッダーメニューを追加します。
login.phpにregister.htmlへのリンクがあるので、そこもregister.phpに修正します。

login.php

    <div class="register">
       <div class="wrapper-title">
         <h4>新規登録</h4>
         <p>登録がお済みでない方はこちらから新規登録してください。</p>
      </div>
-     <button type="button" class="btn btn-submit" onclick="location.href='register.html'">新規登録</button>
+     <button type="button" class="btn btn-submit" onclick="location.href='register.php'">新規登録</button>
    </div>

ログイン状態でindex.phpにアクセスしたときにログアウトボタンが表示されていることを確認してください。
ログアウトしてindex.phpにアクセスしたときにログインボタンが表示されていることを確認してください。

以上で会員機能の追加が完了しました。

変更したファイル名・リンク等

No. ファイル 内容
1 register.html ファイル名変更:register.htmlからregister.phpに変更
2 index.php
page.php
confirm.php
send.php
shop.php
cart.php
pay.php
pay_card.php
pay_conf.php
pay_end.php
register.php
login.php
PHP上部にuser_loginチェックを追加
3 index.php
page.php
confirm.php
send.php
shop.php
cart.php
pay.php
pay_card.php
pay_conf.php
pay_end.php
register.php
login.php
メニューバー変更 :会員登録(register.html)を削除し、ログイン分岐を入れてログイン・ログアウトを表示するように変更
4 login.php 新規登録ボタンリンク変更:register.htmlからregister.phpに変更

コード

https://github.com/bluecode-io/web-basic/tree/basic7-7