購物車是電商核心功能。本文提供一套不依賴資料庫、以 Session + 文字檔儲存商品資料的完整購物車範例,涵蓋加入商品、顯示購物車、修改數量、移除商品、結帳導向支付頁面等完整流程,程式碼結構清晰,適合中小型專案快速導入或教學參考。
| 檔案 | 功能 | 說明 |
|---|---|---|
lib.inc.php | 商品資料載入 | 從 products.txt 讀取商品主檔 |
add_item.php | 加入購物車 | 接收商品 ID、數量,寫入 Session |
cart.php | 購物車頁面 | 顯示明細、修改數量、移除、小計、商品列表 |
change_quant.php | 修改數量 | 更新 Session 中商品數量或移除 |
products.txt | 商品資料檔 | 每行一筆:id|name|desc|price |
products.txtP001|iPhone 15 Pro|最新款旗艦手機|35900
P002|MacBook Air M2|13吋輕薄筆電|32900
P003|AirPods Pro 2|主動降噪藍牙耳機|7490
P004|Apple Watch S9|智慧手錶|12900
P005|iPad Air 10.9|平板電腦|19900
lib.inc.php<?php
$master_products_list = [];
function LoadProducts() {
global $master_products_list;
$filename = 'products.txt';
$fp = @fopen($filename, 'r') or die("無法開啟 $filename");
@flock($fp, LOCK_SH) or die("鎖定失敗");
while ($line = fgets($fp, 1024)) {
$line = trim($line);
if (!$line) continue;
list($id, $name, $desc, $price) = explode('|', $line);
$id = trim($id);
$master_products_list[$id] = [
'name' => $name,
'desc' => $desc,
'price' => (float)$price,
];
}
@fclose($fp);
}
?>
add_item.php<?php
session_start();
require 'lib.inc.php';
LoadProducts();
$prod_id = $_GET['id'] ?? '';
if (!$prod_id || !isset($master_products_list[$prod_id])) {
die('商品不存在');
}
$product = $master_products_list[$prod_id];
// 初始化購物車 Session
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = ['num_items' => 0, 'products' => []];
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$qty = max(1, (int)($_POST['quantity'] ?? 1));
// 檢查是否已在購物車
$found = false;
foreach ($_SESSION['cart']['products'] as $i => $item) {
if ($item[0] === $prod_id) {
$_SESSION['cart']['products'][$i][1] += $qty;
$found = true;
break;
}
}
if (!$found) {
$_SESSION['cart']['products'][] = [$prod_id, $qty];
}
$_SESSION['cart']['num_items'] += $qty;
echo "{$product['name']} 已加入購物車
";
echo '查看購物車 | 繼續購物';
exit;
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>加入購物車</title></head><body>
<h1>加入 <?= htmlspecialchars($product['name']) ?> 到購物車</h1>
<p>說明:<?= htmlspecialchars($product['desc']) ?></p>
<p>單價:NT$ <?= number_format($product['price']) ?></p>
<form method="post">
數量:<input type="number" name="quantity" value="1" min="1" max="99">
<input type="hidden" name="id" value="<?= $prod_id ?>">
<button type="submit">加入購物車</button>
</form>
<p><a href="cart.php">返回商品列表</a></p>
</body></html>
cart.php<?php
session_start();
require 'lib.inc.php';
LoadProducts();
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = ['num_items' => 0, 'products' => []];
}
$cart = &$_SESSION['cart'];
$total = 0;
foreach ($cart['products'] as $item) {
list($pid, $qty) = $item;
if (isset($master_products_list[$pid])) {
$total += $master_products_list[$pid]['price'] * $qty;
}
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>購物車</title>
<style>
table {border-collapse:collapse;width:100%;max-width:800px;margin:1rem auto;}
th,td {border:1px solid #ddd;padding:8px;text-align:center;}
th {background:#f5f5f5;}
.input-qty {width:60px;text-align:center;}
.btn {padding:4px 12px;cursor:pointer;}
</style></head><body>
<h1 style="text-align:center">我的購物車</h1>
<?php if ($cart['num_items'] > 0): ?>
<table>
<thead>
<tr><th>商品</th><th>說明</th><th>單價</th><th>數量</th><th>小計</th><th>操作</th></tr>
</thead>
<tbody>
<?php foreach ($cart['products'] as $idx => $item):
list($pid, $qty) = $item;
if (!isset($master_products_list[$pid])) continue;
$p = $master_products_list[$pid];
$subtotal = $p['price'] * $qty;
?>
<tr>
<td><?= htmlspecialchars($p['name']) ?></td>
<td><?= htmlspecialchars($p['desc']) ?></td>
<td>NT$ <?= number_format($p['price']) ?></td>
<td>
<form action="change_quant.php" method="post" style="display:inline">
<input type="hidden" name="idx" value="<?= $idx ?>">
<input type="number" name="quantity" value="<?= $qty ?>" min="0" max="99" class="input-qty">
<button type="submit" class="btn">更新</button>
</form>
</td>
<td>NT$ <?= number_format($subtotal) ?></td>
<td>
<form action="change_quant.php" method="post" style="display:inline" onsubmit="return confirm('確定移除?')">
<input type="hidden" name="idx" value="<?= $idx ?>">
<input type="hidden" name="quantity" value="0">
<button type="submit" class="btn" style="background:#dc3545;color:white">移除</button>
</form>
</td>
</tr>
<?php endforeach; ?>
<tr>
<td colspan="4" style="text-align:right"><strong>總計</strong></td>
<td><strong>NT$ <?= number_format($total) ?></strong></td>
<td></td>
</tr>
</tbody>
</table>
<div style="text-align:center;margin:1rem">
<a href="checkout.php" class="btn" style="background:#28a745;color:white;padding:12px 24px;font-size:1.1rem">前往結帳</a>
</div>
<?php else: ?>
<p style="text-align:center;color:#666">購物車是空的</p>
<?php endif; ?>
<hr>
<h2>商品列表</h2>
<table>
<thead><tr><th>商品</th><th>說明</th><th>單價</th><th>操作</th></tr></thead>
<tbody>
<?php foreach ($master_products_list as $pid => $p): ?>
<tr>
<td><?= htmlspecialchars($p['name']) ?></td>
<td><?= htmlspecialchars($p['desc']) ?></td>
<td>NT$ <?= number_format($p['price']) ?></td>
<td><a href="add_item.php?id=<?= $pid ?>" class="btn">加入購物車</a></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</body></html>
change_quant.php<?php
session_start();
if (!isset($_SESSION['cart'])) {
header('Location: cart.php');
exit;
}
$idx = (int)($_POST['idx'] ?? -1);
$new_qty = max(0, (int)($_POST['quantity'] ?? 0));
if ($idx < 0 || $idx >= count($_SESSION['cart']['products'])) {
header('Location: cart.php');
exit;
}
$old_qty = $_SESSION['cart']['products'][$idx][1];
if ($new_qty > 0) {
$_SESSION['cart']['products'][$idx][1] = $new_qty;
$_SESSION['cart']['num_items'] += ($new_qty - $old_qty);
} else {
// 數量為 0 視為移除
array_splice($_SESSION['cart']['products'], $idx, 1);
$_SESSION['cart']['num_items'] -= $old_qty;
}
header('Location: cart.php');
?>
| 功能 | 實作方向 |
|---|---|
| 資料庫儲存 | 建立 cart_items 表,user_id 關聯,支援跨裝置同步 |
| Redis 快取 | Session 改用 Redis,高併發下效能更佳、支援集群 |
| 優惠碼/折扣 | 結帳頁面輸入優惠碼,後端驗證並計算折扣金額 |
| 運費計算 | 依地址、重量、金額門檻計算運費,支援多物流商 |
| 庫存檢查 | 加入/修改數量時即時檢查庫存,不足時提示 |
| 購物車持久化 | 未登入用戶用 Cookie 儲存 cart_id,登入後合併 |
| AJAX 無刷新更新 | 前端 fetch API 更新數量,回傳新總計、小計 |
| 商品屬性 (規格) | 支援顏色、尺寸等 SKU,購物車按 SKU 區分 |
htmlspecialchars() 防 XSS(int) 強制轉型、範圍限制session.cookie_httponly=1, session.cookie_secure=1, session.use_strict_mode=1此範例展示了購物車的核心邏輯與資料流,實際專案建議採用成熟電商框架 (如 Laravel Shopping Cart、Sylius、Magento) 或依業務規模自行模組化擴展。