7-2:ショッピングカートを作成する

2019/05/29

概要

商品一覧が作成できたので、ショッピングカート機能を実装していきます。
各商品のカートに入れるボタンをクリックして、カートに商品を入れ、カート内の商品をカートの中身ページで表示できるようにしていきます。

フォルダ階層

完成イメージ

ショッピングカート

全体の手順

手順は以下の通りです。

  1. カートに入れるを実装
  2. カート内の商品の表示を実装
  3. 削除ボタンの実装

カートに入れるを実装

カート内の情報は管理画面のログイン情報と同様にPHPのsessionを利用して保持します。
カートに入れるボタンでフォームを送信し、送信した情報をPHPのsessionで管理するという流れです。

カートに入れるボタンの実装

ファイルの拡張子を変更

PHPでsessionを扱えるようにshop.htmlからshop.phpに変更します。

フォームの追加

各商品ごとにformを作成します。
<div class="item-form">をdivタグからformタグに変更します。
閉じタグも</div>から</form>に変更します。
どのフォームも送り先は自分自身(shop.php)、methodはPOSTです。
商品名と金額をhiddenで送るよう設定します。
下記のサンプルコードはバナナだけですが、すべての商品を同じように設定します。
shop.php

 <li>
    <img src="products/banana.jpg" width="300px" height="200px">
    <div class="item-body">
        <h5>バナナ</h5>
        <p>¥500</p>
-       <div class="item-form">
+       <form action="shop.php" method="POST" class="item-form">
+           <input type="hidden" name="name" value="バナナ">
+           <input type="hidden" name="price" value="500">
-           <input type="text" value="1">
+           <input type="text" value="1" name="count">
            <button type="submit" class="btn-sm btn-blue">カートに入れる</button>
+       </form>
-       </div><!-- end item-form -->
    </div><!-- end item-body-->
 </li>

データ受け取りの設定

フォームで送ったデータを受け取るようにhtmlの上にphpを記述します。
shop.php

<?php

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

?>
<!DOCTYPE html>

sessionに保存

受け取ったデータをsessionに保存します。
データを受け取った場合はproductsという配列に入れます。
productsは
['バナナ'=>{['count']=>バナナの個数,['price']=>バナナの金額]},['トマト'=>{['count']=>トマトの個数,['price']=>トマトの金額]}]
というような形でデータを保存されるようにします。
shop.php

 <?php

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

+   session_start();
    //配列に入れるには、$name,$count,$priceの値が取得できていることが前提なのでif文で空のデータを排除する
+   if($name!=''&&$count!=''&&$price!=''){
+       $_SESSION['products'][$name]=[
+            'count' => $count,
+            'price' => $price
+        ];
+   }
 ?>

sessionの取得

sessionを取得してみます。
カート情報はproductsという名前で保存するように設定したので、$_SESSION['products']でカート情報を取得します。
shop.php

    session_start();
    if($name!=''&&$count!=''&&$price!=''){
        $_SESSION['products'][$name]=[
            'count' => $count,
            'price' => $price
        ];
    }
+ $products = isset($_SESSION['products'])? $_SESSION['products']:[];
?>

$productsに配列で入っているので、foreachを使って表示させてみます。
products内では
['バナナ'=>{['count']=>バナナの個数,['price']=>バナナの金額]},['トマト'=>{['count']=>トマトの個数,['price']=>トマトの金額]}]
という形で保存されています。
バナナ・トマトといった商品名がキー変数に設定されているので、foreachでキーと値の両方取得できるようにします。
例)

foreach(配列変数 as キー変数 => 値変数){
 //
}

という形でキーと値が取得できます。
これを今回の例に当てはめると下記のようになります。
キー変数名を$keyと設定し、値変数を$productとしています。
shop.php

    session_start();
    if($name!=''&&$count!=''&&$price!=''){
        $_SESSION['products'][$name]=[
            'count' => $count,
            'price' => $price
        ];

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

+   if(isset($products)){
+       foreach($products as $key => $product){
+           echo $key;      //商品名
+           echo "<br>";
+           echo $product['count'];  //商品の個数
+           echo "<br>";
+           echo $product['price']; //商品の金額
+           echo "<br>";
+       }
+   }

ブラウザで表示してみます。

簡易的に表示してるだけなので見た目はよくありませんが、メニューバーの上に商品名・個数・金額が保存されていて、取得できました。

sessionに保存されていることと、その情報が取得できることが確認できたので、下記の部分は削除しておきます。
shop.php

-   if(isset($products)){
-       foreach($products as $key => $product){
-           echo $key;      //商品名
-           echo "<br>";
-           echo $product['count'];  //商品の個数
-           echo "<br>";
-           echo $product['price']; //商品の金額
-           echo "<br>";
-       }
-   }

次に、追加でカートに商品を入れたときの対応をしなければいけません。
例えば、最初にバナナを1個カートに入れて、次にバナナを2個カートに入れたら、カートの中は3個になっててほしいです。
現状は一番最新でカートに入れた数がsessionに書き込まれるようになっています。
なので、送られてきた商品がsessionのproductsに既に存在するか確認して、存在する場合は既に入っている個数に足し算をするようにしましょう。

    session_start();
    //もし、sessionにproductsがあったら
+   if(isset($_SESSION['products'])){  
        //$_SESSION['products']を$productsという変数にいれる
+       $products = $_SESSION['products']; 
        //$productsをforeachで回し、キー(商品名)と値(金額・個数)取得
+       foreach($products as $key => $product){  
            //もし、キーとPOSTで受け取った商品名が一致したら、
+           if($key == $name){ 
                //既に商品がカートに入っているので、個数を足し算する     
+               $count = (int)$count + (int)$product['count'];
+           }
+       }
+   }  

    if($name!=''&&$count!=''&&$price!=''){
        $_SESSION['products'][$name]=[
            'count' => $count,
            'price' => $price
        ];
    }
    $products = isset($_SESSION['products'])? $_SESSION['products']:[];

これで商品をカートに入れるが実装できました。

カート内の商品を表示する

カートの中身を表示するページを作成します。
shop.phpをコピーしてcart.phpというファイル名で作成します。
上部のphpは一旦削除します。

HTML作成

titleをカートに変更し、mainタグ内のdivタグwrapper-titleクラスのh3タグ内をMY CART,
pタグ内をカートに変更します。
パンくずリストのliタグもカートに変更します。
itemlist内をすべて削除します。
cart.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="ブログの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="ブログの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>
        <div class="breadcrumbs">
            <div class="container">
                <ul>
                    <li><a href="index.php">TOP</a></li>
                    <li>カート</li>
                </ul>
            </div>
        </div>
        <div class="wrapper last-wrapper">
            <div class="container">
                <div class="wrapper-title">
                    <h3>MY CART</h3>
                    <p>カート</p>
                </div>

            </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>

カートの中身をテーブルで表示させるので表示を仮で作ってみます。
データは後でsessionを使って表示させるので今はダミーです。
cart.php

 <div class="container">
    <div class="wrapper-title">
        <h3>MY CART</h3>
        <p>カート</p>
    </div>
+   <div class="cartlist">
+       <table class="cart-table">
+           <thead>
+               <tr>
+                   <th>商品名</th>
+                   <th>価格</th>
+                   <th>個数</th>
+                   <th>小計</th>
+                   <th>操作</th>
+               </tr>
+           </thead>
+           <tbody>
+               <tr>
+                   <td label="商品名:">バナナ</td>
+                   <td label="価格:" class="text-right">¥500</td>
+                   <td label="個数:" class="text-right">2</td>
+                   <td label="小計:" class="text-right">¥1,000</td>
+                   <td>
+                       <button type="button" class="btn btn-red">削除</button>
+                   </td>
+               </tr>
+               <tr class="total">
+                   <th colspan="3">合計</th>
+                   <td colspan="2">0</td>
+               </tr>
+           </tbody>
+       </table>
+   </div>
 </div>

その下に「購入手続きへ」ボタンと「お買い物を続ける」ボタンを付けます。
cart.php

 <div class="container">
    <div class="wrapper-title">
        <h3>MY CART</h3>
        <p>カート</p>
    </div>
    <div class="cartlist">
        <table class="cart-table">
            <thead>
                <tr>
                    <th>商品名</th>
                    <th>価格</th>
                    <th>個数</th>
                    <th>小計</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td label="商品名:">バナナ</td>
                    <td label="価格:" class="text-right">¥500</td>
                    <td label="個数:" class="text-right">2</td>
                    <td  label="小計:" class="text-right">¥1,000</td>
                    <td>
                        <button type="button" class="btn btn-red">削除</button>
                    </td>
                </tr>
                <tr class="total">
                    <th colspan="3">合計</th>
                    <td colspan="2">0</td>
                </tr>
            </tbody>
        </table>
+       <div class="cart-btn">
+           <button type="button" class="btn btn-blue">購入手続きへ</button>
+           <button type="button" class="btn btn-gray">お買い物を続ける</button>
+       </div>
    </div>
 </div>

cssも追加します。
.btn-redは共通css内に追加します(共通css内ならどこでもいいですが、一番最後に追加するといいでしょう)
styles.css

/* 共通CSS */
.btn-red {
    border: 1px solid red;
    color: red;
    padding: 5px 10px;
    font-size: 15px;
}

/* cart */
.cart-table {
    width: 100%;
    border-collapse: collapse;
}

.text-right {
    text-align: right;
}

    .cart-table th,
    .cart-table td {
        border: 1px solid #ebeced;
        vertical-align: middle;
        padding: 5px 10px;
    }

    .cart-table th {
        background-color: #4c586f;
        color: #fff;
    }

    .cart-table button {
        margin: 0;
    }

    .total td{ 
        text-align: center;
    }

    .cart-btn {
        text-align: center;
    }

responsive.css

    /* cart.html */
    .cart-table {
        width: 100%;
        margin: 0 auto;
    }

    .cart-table thead {
        display: none;
    }

    .cart-table th,
    .cart-table td {
        border: none;
        padding: 5px 10px;
        text-align: left;
    }

    .cart-table td form {
        text-align: right;
        width: 90%;
    }
    .cart-table tbody th {
        display: block;
        text-align: center;
    }

    .cart-table tbody td::before {
        content: attr(label);
        float: left;
        clear:both;
        font-weight:bold;
    }

    .cart-table tr {
        border: 1px solid #ebeced;
    }

    .cart-btn .btn{
        margin-top: 20px;
    }

    .total td{
        font-weight: bold;
        text-align: center;
        padding: 5px 0;
    }

cart.phpにアクセスしてみましょう。

テーブルとボタンがこのように表示されていればOKです。

sessionの取得

sessionの中身表示

shop.phpで保存したsessionを取得します。
HTMLの上でphpのsessionを取得します。
session_start()でsessionを開始します。
カート情報はproductsという名前で保存するように設定したので、$_SESSION['products']でカート情報を取得します。
カートが空の場合もあるので、その場合は空の配列を返すようにします。
cart.php

<?php
     session_start();
     $products = isset($_SESSION['products'])? $_SESSION['products']:[];
?>
<!DOCTYPE html>

配列で入っているので、foreachを使ってHTMLのテーブル内で表示させてみます。
products内では
['バナナ'=>{['count']=>バナナの個数,['price']=>バナナの金額]},['トマト'=>{['count']=>トマトの個数,['price']=>トマトの金額]}]
という形で保存されています。
バナナ・トマトといった商品名がキー変数に設定されているので、foreachでキーと値両方取得できるようにします。
例)

foreach(配列変数 as キー変数 => 値変数){
 //
}

という形でキーと値が取得できます。
これを今回の例に当てはめると下記のようになります。
小計は取得したcountとpriceを掛け算したものです。
例)

foreach($products as $name => $product){
     echo $name;      //商品名
     echo $name['count'];  //商品の個数
     echo $name['price']; //商品の価格
     echo (int)$name['price']*(int)$name['count']; //商品の小計(文字列ではなく数値であることを表す(int)をつける)
}

これをHTMLテーブルに入れていきます。
cart.php

 <tbody>
+   <?php foreach($products as $name => $product): ?>
    <tr>
-       <td label="商品名:">バナナ</td>    
-       <td label="価格:" class="text-right">¥500</td>
-       <td label="個数:" class="text-right">2</td>
-       <td label="小計:" class="text-right">¥1,000</td>
+       <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>
            <button type="button" class="btn btn-red">削除</button>
        </td>
    </tr>
+   <?php endforeach; ?>
    <tr class="total">
        <th colspan="3">合計</th>
        <td colspan="2">0</td>
    </tr>
 </tbody>

一旦確認します。

カートに入れた商品名・価格・個数・小計が正しく表示されているかを確認します。

合計金額の表示

商品ごとの表示は正しく表示できていると思いますが、合計が表示されていないので、合計を計算して表示させていきます。
HTML内のforeachで記述してもいいのですが、少しごちゃごちゃしそうなので、合計は上のPHPで計算することにします。
まず、$totalという変数を作成して、初期値に0を設定します。
cart.php

 <?php 

    session_start();

    //合計の初期値は0
+   $total = 0; 

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

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

$totalに合計金額が入ったので、HTMLで表示してみます。
cart.php

 <tr>
    <th colspan="3">合計</th>
+   <td colspan="2">¥<?php echo $total; ?></td>
-   <td colspan="2">0</td>
 </tr>

確認します。

合計も正しく表示されればカート機能は実装完了です。

削除ボタンの実装

カートに入れた商品を削除できるようにします。
削除ボタンを表示しているtdタグにformタグを追加していきます。
送り先は自分自身で、methodはPOSTです。
削除する商品名をdelete_nameという名前で送るように設定しています。
cart.php

 <td>
+   <form action="cart.php" method="post">
+   <input type="hidden" name="delete_name" value="<?php echo $name; ?>">
-   <button type="button" class="btn btn-red">削除</button>
+   <button type="submit" class="btn btn-red">削除</button>
+   </form>
 </td>

POSTで送られてきたデータを頭のPHPで受け取ります。
受け取ったら、session_start()のあとで、商品名を指定して、sessionから削除するようにします。
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 += $subtotal;
    }
 ?>

アクセスして動作確認します。
削除ボタンを押して、商品が削除されればOKです。
これで削除ボタンの実装も完了しました。

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

No. ファイル 内容
1 index.php メニューバー変更 :shop.htmlをshop.phpに変更
2 page.php メニューバー変更 :shop.htmlをshop.phpに変更
3 confirm.php メニューバー変更 :shop.htmlをshop.phpに変更
4 send.php メニューバー変更 :shop.htmlをshop.phpに変更
5 shop.php メニューバー変更 :shop.htmlをshop.phpに変更
6 register.html メニューバー変更 :shop.htmlをshop.phpに変更
7 regi_conf.php メニューバー変更 :shop.htmlをshop.phpに変更
8 regi_end.php メニューバー変更 :shop.htmlをshop.phpに変更

コード

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