PHPでショッピングカートの合計を計算してみよう(総合演習)
ここまでの PHP レッスンで学んだことを全部つないで、ECサイトの心臓部を作ります。新しい構文は出てきません。配列・foreach・関数・if文——すでに練習したものだけで組み立てます。
作るのは買い物かごの金額計算です。小計を出し、送料無料の判定をして、消費税を足し、レシートの形に整えます。金額の計算は、Webの仕事でいちばん間違えてはいけないところです。
このページではコードを実行していません。PHPはサーバー側で動く言語なので、ブラウザの中では走らせられないからです。かわりにお手本と同じコードを書けたかを、その場で判定します(判定はあなたのブラウザの中だけで行われ、どこにも送信されません)。
表示している実行結果は本物です。このページを作るときに、実際にPHPで走らせて得た出力をそのまま載せています。
完成イメージ
最後まで進むと、このコードが完成します。下の実行結果は、このページを作るときに開発機で実際にコードを走らせて得たものです(このページ上では実行していません)。
===== ご注文内容 ===== ワイヤレスマウス x2 5,960円 メカニカルキーボード x1 12,800円 USB-Cハブ x3 13,500円 ノートPCスタンド x1 3,800円 ---------------------------------------- 小計 36,060円 送料 0円 消費税 3,606円 合計 39,666円 送料無料が適用されました。
STEP 1カートの中身を配列で表す
🎯 このステップのゴール: 商品1件を連想配列にし、それを並べた配列を作る
まずデータの形を決めます。買い物かごの1行は「商品名・単価・個数」の3つを持ちます。
PHPでは連想配列——"name" => "..."のようにキーと値の組で書ける配列——を使います。番号ではなく名前で取り出せるので、$item["price"]と書けば何のことか一目で分かります。
そして1回の注文には何行も入るので、連想配列を並べた配列にします。この「連想配列の配列」という形は、PHPで表形式のデータを扱うときの基本形です。データベースから取り出した結果も、たいていこの形で返ってきます。
count()で件数を数えて、正しく作れたか確かめます。[]という短い書き方は PHP 5.4 以降のもので、古いコードではarray()と書かれています——意味は同じです。
商品数: 4件 1件目: ワイヤレスマウス
STEP 2小計を計算して関数にする
🎯 このステップのゴール: foreachで金額を合計し、関数として切り出す
小計を出します。1件ずつ単価 × 個数を求めて足していきます。
使うのはforeachです。forで添字を回すこともできますが、「全件に対して順に処理する」ならforeachのほうが読みやすく、添字のずれも起きません。PHPで配列を回すときの第一選択です。
そしてこれを関数に切り出します。function subtotal(array $cart): int——引数と戻り値に型を書いているのがポイントです。PHP 7以降で書けるようになった型宣言で、うっかり文字列を渡したときにその場でエラーになります。
金額を扱うコードほど、型を書く価値があります。数字のつもりが文字列だった、という取り違えは金額計算で最も怖い種類の不具合です。
合計する前に$sum = 0;と入れ物を用意するのを忘れないでください。
小計: 36060円
STEP 3送料無料の判定を入れる
🎯 このステップのゴール: if文で条件分岐し、しきい値を定数にする
「1万5千円以上で送料無料」という、よくある条件を入れます。
ここで数字を直接書かないのが要点です。if ($subtotal >= 15000)と書いてしまうと、この15000が何を意味するのか後から読んで分かりません。こういう意味の分からない数字を「マジックナンバー」と呼び、避けるべきものとされています。
const FREE_SHIPPING = 15000;と定数にしておけば、名前が意味を語ってくれます。しかもキャンペーンでしきい値を変えるとき、1か所直すだけで済みます。
判定は三項演算子で1行にしました。$subtotal >= FREE_SHIPPING ? 0 : SHIPPING_FEE——「条件を満たすなら0円、そうでなければ800円」と読めます。if文で書いても構いません。
PHPの定数はconstのほかdefine()でも作れます。関数の外で使うならconstのほうが簡潔です。
小計: 36060円 送料: 0円
STEP 4消費税を計算する
🎯 このステップのゴール: 端数の切り捨てを明示し、整数で扱う
消費税を足します。ここが金額計算でいちばん神経を使うところです。
($sub + $ship) * 0.1を計算すると小数になります。きれいに割り切れる額ならいいのですが、たいていは3606.0000000004のような値になります。1円未満をどう扱うか決めないと、金額がずれます。
日本の商習慣では切り捨てが一般的なので、floor()を使います。そして(int)で整数に変換します。お金は整数で持つ——これが鉄則です。小数のまま持ち回すと、足し合わせるうちに誤差が積もります。
切り上げならceil()、四捨五入ならround()です。どれを使うかは仕様で決めるべきことで、プログラマが勝手に選ぶものではありません。実務では必ず確認します。
税率もconst TAX_RATE = 0.1;と定数にします。税率は変わるものだからです。
小計: 36060円 送料: 0円 消費税: 3606円 合計: 39666円
STEP 5レシートの形に整える
🎯 このステップのゴール: printfとnumber_formatで桁を揃えて見やすくする
数字が並んでいるだけでは読めません。レシートのように整えます。
number_format()は3桁ごとにカンマを入れる関数です。33060が33,060になります。金額を表示するときは、ほぼ必ず通します。
桁を揃えるのがprintf()です。%-22sは「左揃えで22文字ぶん」、%8sは「右揃えで8文字ぶん」、%dは整数。商品名は左、金額は右に揃えると読みやすくなります。
ここで日本語の幅に注意が要ります。%-22sはバイト数で数えるので、日本語が混じると見た目の位置は完全には揃いません。それでも大きくは整うので実用になりますが、厳密に揃えたいならHTMLの表を使うのが本筋です。
str_repeat("-", 40)は同じ文字を繰り返す関数で、区切り線を引くのに使います。
===== ご注文内容 ===== ワイヤレスマウス x2 5,960円 メカニカルキーボード x1 12,800円 USB-Cハブ x3 13,500円 ノートPCスタンド x1 3,800円 ----------------------------------------
STEP 6ひとつの関数にまとめて仕上げる
🎯 このステップのゴール: 全部を printReceipt() に集約し、案内文まで出す
最後の仕上げです。ここまでの部品をprintReceipt()という1つの関数にまとめます。
戻り値の型に: voidと書いているのに注目してください。「この関数は値を返さない、表示するのが仕事だ」と宣言しています。読む人が戻り値を期待しなくなるので、意図が伝わります。
そしてもう一押しの工夫を入れます。送料がかかる場合に「あと〇〇円で送料無料です」と出すのです。FREE_SHIPPING - $subで不足額を計算するだけですが、実際のECサイトでこの一言があるかないかで、購入額が変わります。
完成です。$cartの中身を書き換えて、合計が15000円を下回るようにしてみてください。送料が800円になり、案内文のほうが表示されるはずです。
数字を変えたときにどこが連動して変わるかを追ってみると、関数に切り出した意味が見えてきます。
===== ご注文内容 ===== ワイヤレスマウス x2 5,960円 メカニカルキーボード x1 12,800円 USB-Cハブ x3 13,500円 ノートPCスタンド x1 3,800円 ---------------------------------------- 小計 36,060円 送料 0円 消費税 3,606円 合計 39,666円 送料無料が適用されました。
よくある質問
このページではコードを実行しないのですか?
していません。PHPはサーバー側で動く言語なので、ブラウザの中だけでは実行できないためです。このページではお手本と同じコードを書けたかどうかを判定する形にしています(判定はあなたのブラウザの中だけで行われ、コードはどこにも送信されません)。
ただし表示している実行結果は本物です。このページを作るときに実際のPHPで走らせて得た出力を、そのまま載せています。自分のパソコンにPHPを入れれば、まったく同じ結果が出ます。
消費税は切り捨てで合っていますか?
仕様によります。日本の商習慣では切り捨てが一般的ですが、切り上げや四捨五入を採用している事業者もあります。
大事なのは「決めて、コードに明示する」ことです。floor()と書いてあれば、後から読む人にも意図が伝わります。何も書かずに小数のまま扱うのが、いちばん危険です。
実際のECサイトではどう作りますか?
考え方は同じですが、カートの中身はデータベースやセッションから来ます。商品の値段も配列に直接書くのではなく、商品テーブルから引いてきます。
また値段は必ずサーバー側で計算し直します。ブラウザから送られてきた金額をそのまま信じると、改ざんされた値で決済されてしまうためです。金額の計算は、必ずサーバーの責任です。
🚩 次はこのカテゴリへ
カートの計算ができたら、次はその商品データがどこから来るかです。実際のECサイトでは、商品も注文もデータベースに入っています。SQLカテゴリの最終演習では、まさにその注文データから売上を集計します。
SQLのレッスンへ進む